React+TypeScript中测试VideoPlayer组件错误日志分支的问题
如何测试VideoPlayer组件中未覆盖的错误分支?
我开发了一个基于React与TypeScript的VideoPlayer组件,功能运行正常,但无法对console.log("there was an error playing the video");这行代码进行测试。尝试通过让videoRef.current不存在来触发该分支但未成功,覆盖率报告显示第13行未覆盖,请求指导如何完成该分支的测试。
组件代码
import PlayArrowIcon from "@mui/icons-material/PlayArrow"; import { get } from "lodash"; import { useRef, useState } from "react"; const VideoPlayer = () => { const [showPlayButton, setShowPlayButton] = useState(true); const videoRef = useRef<HTMLVideoElement>(null); const togglePlay = () => { setShowPlayButton((current) => { if (current === true) { const playAction = get(videoRef, "current.play", () => { console.log("there was an error playing the video"); }); playAction(); } return false; }); }; return ( <div onClick={togglePlay} data-testid="player-container"> {showPlayButton && ( <button aria-label="Player button" data-testid="player-button"> <PlayArrowIcon sx={{ fontSize: "2rem" }} /> </button> )} <video ref={videoRef} data-cy="player-video" className="player-video" data-testid="player-video" controls={!showPlayButton} style={{ objectFit: "cover" }} src="https://google.com/video-file.mp4" poster="https://google.com/video-image.jpg" /> </div> ); }; export default VideoPlayer;
当前测试代码
import { fireEvent, render, waitFor } from "@testing-library/react"; import VideoPlayer from "../components/video-player"; describe("VideoPlayer Component", () => { let playStub: jest.SpyInstance<Promise<void>, [], any>; let consoleSpy: jest.SpyInstance; beforeEach(() => { playStub = jest.spyOn(window.HTMLMediaElement.prototype, "play"); consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {}); }); afterEach(() => { playStub.mockRestore(); consoleSpy.mockRestore(); }); it("clicking video should make it play and hide arrow button", async () => { playStub.mockResolvedValue(); const { getByTestId, queryByTestId } = render(<VideoPlayer />); const playButtonStart = getByTestId("player-button"); expect(playButtonStart).toBeInTheDocument(); const videoPlayerStart = getByTestId("player-container"); fireEvent.click(videoPlayerStart); await waitFor(() => expect(playStub).toHaveBeenCalled()); const playButtonFinal = queryByTestId("player-button"); expect(playButtonFinal).not.toBeInTheDocument(); fireEvent.click(videoPlayerStart); expect(playStub).toHaveBeenCalledTimes(1); }); });
覆盖率报告
yarn test yarn run v1.22.19 warning package.json: No license field $ cross-env BABEL_ENV=test jest --coverage PASS src/__tests__/video-player.test.tsx ---------------------|---------|----------|---------|---------|------------------- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ---------------------|---------|----------|---------|---------|------------------- All files | 98.79 | 100 | 93.33 | 98.78 | src/components | 98.07 | 100 | 90 | 98.03 | video-player.tsx | 93.33 | 100 | 75 | 93.33 | 13 ---------------------|---------|----------|---------|---------|-------------------
解决方案
为什么之前的尝试失败?
React渲染组件时会自动将<video>元素实例赋值给videoRef.current,所以组件挂载后该引用不可能为空,这就是你尝试让videoRef.current不存在却无法触发分支的原因。要触发目标分支,需要让lodash.get无法获取到videoRef.current.play方法,有两种可行方案:
方案一:Mock lodash.get 方法
直接模拟lodash.get在获取current.play路径时返回默认的错误处理函数:
it("should log error when play method is unavailable via lodash.get mock", () => { const originalGet = jest.requireActual("lodash").get; const getSpy = jest.spyOn(require("lodash"), "get").mockImplementation((obj, path, defaultValue) => { if (path === "current.play") { return defaultValue; } return originalGet(obj, path, defaultValue); }); const { getByTestId } = render(<VideoPlayer />); fireEvent.click(getByTestId("player-container")); expect(consoleSpy).toHaveBeenCalledWith("there was an error playing the video"); getSpy.mockRestore(); });
方案二:临时移除HTMLMediaElement的play方法
通过删除HTMLMediaElement.prototype.play,让videoRef.current.play不存在,从而触发默认分支:
it("should log error when play method doesn't exist", () => { // 临时删除play方法 const originalPlay = window.HTMLMediaElement.prototype.play; delete window.HTMLMediaElement.prototype.play; const { getByTestId } = render(<VideoPlayer />); fireEvent.click(getByTestId("player-container")); expect(consoleSpy).toHaveBeenCalledWith("there was an error playing the video"); // 恢复原方法 window.HTMLMediaElement.prototype.play = originalPlay; });
把这两个测试用例添加到你的测试文件中,即可覆盖第13行的代码分支。
内容的提问来源于stack exchange,提问作者LuisEnMarroquin
相关产品推荐
相关产品推荐

