You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 13:45:23