【问题标题】:How to use jest.spyOn with React function component using Typescript如何使用 Typescript 将 jest.spyOn 与 React 函数组件一起使用
【发布时间】:2019-10-11 08:12:46
【问题描述】:

我正在使用 Typescript 和 hooks 开发一个 React 应用程序,并且我正在尝试使用 Enzyme 和 Jest 来测试功能组件。我无法使用 jest.spyOn 来测试我的组件中的方法。 jest.spyOn 方法无法正确解析并在悬停时显示以下消息

“'"validateBeforeSave"' 类型的参数不可分配给 '"context" 类型的参数 | “设置状态” | "强制更新" | “渲染” | "componentDidMount" | “应该组件更新” | “组件将卸载” | "componentDidCatch" | "getSnapshotBeforeUpdate" | ... 6 更多... | "UNSAFE_componentWillUpdate"'.ts(2345)"

我尝试将实例转换为“任何” -

const instance = wrapper.instance() as any;

这当然忽略了编译时的问题,但随后测试会抛出组件上不存在函数的运行时错误。

无法监视 validateBeforeSave 属性,因为它不是 功能;取而代之的是未定义

// Some function Component

const SomeComponent = (props: IMyComponentProps) => {
  const { classes } = props;

  // Component has state
  const [count, setCount] = useState(0);

  function validateBeforeSave(){

  }

  function handleClick() {
  validateBeforeSave();
  .
  .
  .
  }

  return (
   <div>
      <Button>
      className="saveBtn"
      onClick={handleClick}
      </Button>
    </div>
  );

  };

  // Unit test
  describe('SomeComponent' () => {
  it('validates model on button click', () => {
      const wrapper = mount(
        <MuiThemeProvider theme={theme}>
          <SomeComponent/>
        </MuiThemeProvider>,
      );
  const instance = wrapper.instance();
      const spy = jest.spyOn(instance, "validateBeforeSave");
  wrapper
        .find('.saveBtn')
        .at(0)
        .simulate('click');
      expect(spy).toHaveBeenCalledTimes(1);
    });
  }

我在这里缺少什么? spyOn 如何处理函数组件?

我使用 create-react-app 模板创建了应用程序,它具有测试包的这些依赖项

"devDependencies": {
    "ts-jest": "^23.10.3",
    "@types/jest": "24.0.9",
    "@types/enzyme": "^3.9.1",
    "@types/enzyme-adapter-react-16": "^1.0.2",
    "enzyme": "^3.9.0",
    "enzyme-adapter-react-16": "^1.11.2",
    "enzyme-to-json": "^3.3.5",
  }

【问题讨论】:

    标签: reactjs typescript jestjs enzyme ts-jest


    【解决方案1】:

    您的 validateBeforeSave 函数在 SomeComponent 中声明,使其成为外部无法访问的封闭/私有范围函数。您可以将该函数作为道具传递,然后您可以创建间谍并将其作为道具值在您的测试中传递,并测试传递的道具函数(间谍)是否被调用

    所以你可以像这样修改你的函数:

    // some validator function
    function validateBeforeSave(){
      ...
    }
    
    // Some function Component
    
    const SomeComponent = (props: IMyComponentProps) => {
      const { classes, validateBeforeSave } = props;
    
      // Component has state
      const [count, setCount] = useState(0);
    
    
      function handleClick() {
      validateBeforeSave();
      .
      .
      .
      }
    
      return (
       <div>
          <Button>
          className="saveBtn"
          onClick={handleClick}
          </Button>
        </div>
      );
    
    };
    

    在您的单元测试中,类似这样的内容:

      // Unit test
      describe('SomeComponent' () => {
      it('validates model on button click', () => {
          const validateSpy = jest.fn();
          const wrapper = mount(
            <MuiThemeProvider theme={theme}>
              <SomeComponent validateSpy={validateSpy}/>
            </MuiThemeProvider>,
          );
          const instance = wrapper.instance();
          wrapper
            .find('.saveBtn')
            .at(0)
            .simulate('click');
          expect(validateSpy).toHaveBeenCalledTimes(1);
        });
      }
    

    【讨论】:

    • 感谢您的澄清。所以,我们不能在函数组件中窥探函数。但是,当我尝试在 spyOn 中提供一个函数作为参数时,为什么会出现类型错误?如上所示,我必须将我的实例转换为“任何”,否则我会得到如上所示的编译错误(类型参数..)。我无法使用 spyOn 方法,它喜欢的方法参数的唯一参数是上面错误消息中显示的参数('"context" | "setState" | "forceUpdate" ..etc)。这意味着即使它作为道具传递,我也无法监视任何随机方法。我猜我应该使用 Jest.fn
    • 是的,我在代码中将实现更改为 jest.fn()。如果您从某个地方导入该函数然后对其进行断言,您仍然可以使用 spyOn。
    【解决方案2】:

    我也面临同样的问题 - 我确实喜欢下面 -

    import * as React from 'react';
    
    const SampleComponent = () => {
      const sampleMethod = () => {
        console.log('hello world');
      };
    
      return <button onClick={sampleMethod} type="button">Click Me</button>;
    };
    
    export default SampleComponent;
    

    测试-

    import React from 'react';
    import SampleComponent from './';
    import { shallow } from 'enzyme';
    
    describe('SampleComponent', () => {
      test('should handle click correctly', () => {
        const logSpy = jest.spyOn(console, 'log');
        const wrapper = shallow(<SampleComponent></SampleComponent>);
        const button = wrapper.find('button');
        expect(button.text()).toBe('Click Me');
        button.simulate('click');
        expect(logSpy).toBeCalledWith('hello world');
      });
    });
    

    我们可以窥探console.log,判断它是否被调用

    检查 - https://stackoverflow.com/a/58637912/10734622

    【讨论】:

    • 但是这个解决方案是在功能组件中监视 console.log 而不是 sampleMethod 本身。
    • 这是控制台日志
    【解决方案3】:

    我在使用 React 16.x.x 模拟回调 prop 方法时遇到了类似的问题,酶实例方法返回 null,你可以直接将 jest.fn() 作为 prop 传递。

    示例:

      it('should invoke callback with proper data upon checkbox click', () => {
        const spyCheckboxClick = jest.fn((id, item) => ({
          id,
          item,
        }))
        const component: any = enzyme.mount(
          <SectionColumn {...{
            ...mockProps,
            onCheckboxClick: spyCheckboxClick,
          }} />
        );
        expect(spyCheckboxClick).toHaveBeenCalledTimes(0);
        // perform click to checkbox
        const checkboxComponent = component.find('StyledCheckbox');
        const input = checkboxComponent.first().children();
        input.simulate('change');
        expect(spyCheckboxClick).toHaveBeenCalledTimes(1);
        expect(spyCheckboxClick()).toEqual(null)
     });
    

    【讨论】:

      猜你喜欢
      • 2019-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-05
      • 1970-01-01
      • 2020-07-11
      • 2019-07-30
      • 1970-01-01
      相关资源
      最近更新 更多