关于reactjs:如何使用Typescript将jest.spyOn与React函数组件一起使用

How to use jest.spyOn with React function component using Typescript

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

"Argument of type '"validateBeforeSave"' is not assignable to
parameter of type '"context" |"setState" |"forceUpdate" |"render" |
"componentDidMount" |"shouldComponentUpdate" |"componentWillUnmount"
|"componentDidCatch" |"getSnapshotBeforeUpdate" | ... 6 more ... |
"UNSAFE_componentWillUpdate"'.ts(2345)"

我试图将实例转换为"任何"-

1
const instance = wrapper.instance() as any;

当然,这会在编译时忽略该问题,但是测试会抛出运行时错误,表明该功能在组件上不存在。

Cannot spy the validateBeforeSave property because it is not a
function; undefined given instead

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Some function Component

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

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

  function validateBeforeSave(){

  }

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

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

  };

  // 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模板创建了应用程序,它具有测试包的这些依赖关系

1
2
3
4
5
6
7
8
9
"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",
  }


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

1
2
3
4
5
6
7
8
9
10
11
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;

测试-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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


在此处再次发表我的评论以回答您的问题:validateBeforeSave函数在SomeComponent中声明,从而使其成为无法在外部访问的封闭/私有范围函数。 您可以将该函数作为prop传递,然后可以创建spy并将其作为prop值传递给测试,并测试是否通过了(spy)的prop函数。

因此,您可以像下面这样修改函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 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 (
   
      <Button>
      className="saveBtn"
      onClick={handleClick}
      </Button>
   
  );

};

在单元测试中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  // 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);
    });
  }