如何使用 Sinon 和 ReactJS 模拟模块导入

How to mock a module import with Sinon and ReactJS

我正在尝试为我用 TS 编写的 React 组件之一编写单元测试:

import React, { useContext } from 'react';
import Lottie from 'lottie-react-web';

import { ConfigContext } from '../ConfigProvider';
import type { UIKitFC } from '../../types/react-extensions';

// interfaces
export interface LoadingOverlayProps {
  size: 'large' | 'medium' | 'small';
  testId?: string;
}

interface LoaderProps {
  size: 'large' | 'medium' | 'small';
}

const G3Loader: React.FC<LoaderProps> = ({ size }) => {
  const options = { animationData };
  const pxSize =
    size === 'small' ? '100px' : size === 'medium' ? '200px' : '300px';
  const height = pxSize,
    width = pxSize;

  return (
    <div className="loader-container">
      <Lottie options={options} height={height} width={width} />
      <div className="loader__loading-txt">
        <div>
          <h4>Loading...</h4>
        </div>
      </div>
    </div>
  );
};



/**
 * Description of Loading Overlay component
 */
export const LoadingOverlay: UIKitFC<LoadingOverlayProps> = (props) => {
  const { testId } = props;

  const { namespace } = useContext(ConfigContext);
  const { baseClassName } = LoadingOverlay.constants;

  const componentClassName = `${namespace}-${baseClassName}`;

  const componentTestId = testId || `${namespace}-${baseClassName}`;

  return (
    <div id={componentTestId} className={componentClassName}>
      <G3Loader size={props.size} />
    </div>
  );
};

LoadingOverlay.constants = {
  baseClassName: 'loadingOverlay',
};

LoadingOverlay.defaultProps = {
  testId: 'loadingOverlay',
};

export default LoadingOverlay;

该组件使用导入的模块“Lottie”来制作一些动画,但我对测试它不感兴趣,我只想测试我的组件及其道具。

问题是,当我 运行 我的单元测试时,出现错误: 错误:未实现:HTMLCanvasElement.prototype.getContext(未安装 canvas npm 包)

经过一些研究,我得出结论,错误是由 Lottie 导入引起的,因此我想模拟它以进行测试。我正在使用 Mocha 和 Sinon 的存根功能来尝试模拟库导入,但同样的错误仍然存​​在,让我觉得我没有正确地存根模块。这是我最近的单元测试尝试:

import React from 'react';
import * as Lottie from 'lottie-react-web';
import { render } from '@testing-library/react';
import { expect } from 'chai';
import * as sinon from 'sinon';

import LoadingOverlay from '../src/components/LoadingOverlay';

const TEST_ID = 'the-test-id';

const FakeLottie: React.FC = (props) => {
  return <div>{props}</div>;
};

describe('Loading Overlay', () => {
  //   beforeEach(function () {
  //     sinon.stub(Lottie, 'default').callsFake((props) => FakeLottie(props));
  //   });

  console.log('11111');
  it('should have a test ID', () => {
    sinon.stub(Lottie, 'default').callsFake((props) => FakeLottie(props));
    console.log(Lottie);
    const { getByTestId, debug } = render(
      <LoadingOverlay testId={TEST_ID} size="small" />
    );

    debug();
    expect(getByTestId(TEST_ID)).to.not.equal(null);
  });
});

我不太确定还能尝试什么,单元测试不是我的强项...如果有人能提供帮助,那就太好了。

我回答了我自己的问题...发帖以防其他人遇到同样的问题。

有关 HTMLCanvasElement 的错误。事实证明,我试图存根的组件使用的是 Canvas 库本身,在浏览器中使用 运行 时不需要,但因为我是构建测试,我只是将 Canvas 库添加到我的包中,问题就解决了。完整代码如下:

import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { expect, assert } from 'chai';
import * as lottie from 'lottie-react-web';
import { createSandbox } from 'sinon';

import LoadingOverlay from '../src/components/LoadingOverlay';

// test ID
const TEST_ID = 'the-test-id';

// mocks
const sandbox = createSandbox();
const MockLottie = () => 'Mock Lottie';

describe('Loading Overlay', () => {
  beforeEach(() => {
    sandbox.stub(lottie, 'default').callsFake(MockLottie);
  });

  afterEach(() => {
    sandbox.restore();
    cleanup();
  });

  it('should have test ID', () => {
    const { getByTestId } = render(
      <LoadingOverlay testId={TEST_ID} size="medium" />
    );
    expect(getByTestId(TEST_ID)).to.not.equal(null);
  });
});