如何在功能组件 jest 酶中实现替代功能

How to make a substitute function in a functional component jest enzyme

开个玩笑,我想检查函数是否被调用以及在我的功能组件中是否调用了调度方法。我不能使间谍功能,因为它是一个功能组件,我无权访问实例对象。

import React, { Component } from 'react';
import { useDispatch } from 'react-redux';
import styles from './style.module.scss';

const AddTask = () => {
  const dispatch = useDispatch();
  const handleKeyDown = (e) => {
    const trimmedText = e.target.value.trim();
    if (e.which === 13 && trimmedText) {
      dispatch({ type: 'todoAdd', payload: trimmedText });
      e.target.value = '';
    }
  };

  return (
    <div className={styles.container}>
      <span className={styles.arrow} />
      <input
        className='new-todo'
        placeholder='What needs to be done?'
        onKeyDown={handleKeyDown}
      />
    </div>
  );
};

export default AddTask;


import { shallow } from 'enzyme';
import React from 'react';
import { useDispatch } from 'react-redux';
import AddTask from './AddTask';

jest.mock('react-redux');
let wrapper;

describe('Task:', () => {
  beforeEach(() => {
    wrapper = shallow(<AddTask />);
  });

  it('snapshot', () => {
    expect(wrapper).toMatchSnapshot();
  });

  it('called dispatch', () => {
    expect(useDispatch).toBeCalled(0);
  });
  it('called handleKeyDown fn', () => {
    wrapper.find('input').props().onKeyDown({ key: 'Enter' });
    const onKeyDown = jest.fn();
    expect(onKeyDown).toBeCalled();
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Component Test

你应该只模拟 useDispatch 钩子,使用 jest.requireActual(moduleName) 方法要求原始模块不被模拟。

此外,你最好使用.simulate(event[, mock]) => Self方法在包装器中模拟节点上的事件。

最后,使用jest.resetAllMocks()重置所有模拟。

例如

AddTask.tsx:

import React from 'react';
import { useDispatch } from 'react-redux';

const AddTask = () => {
  const dispatch = useDispatch();

  const handleKeyDown = (e) => {
    const trimmedText = e.target.value.trim();
    if (e.which === 13 && trimmedText) {
      dispatch({ type: 'todoAdd', payload: trimmedText });
      e.target.value = '';
    }
  };

  return (
    <div>
      <input className="new-todo" placeholder="What needs to be done?" onKeyDown={handleKeyDown} />
    </div>
  );
};

export default AddTask;

AddTask.test.tsx:

import { shallow, ShallowWrapper } from 'enzyme';
import React from 'react';
import { useDispatch } from 'react-redux';
import AddTask from './AddTask';

const mDispatch = jest.fn();

jest.mock('react-redux', () => {
  const originalModule = jest.requireActual('react-redux');
  return {
    ...originalModule,
    useDispatch: jest.fn(() => mDispatch),
  };
});

describe('Task:', () => {
  let wrapper: ShallowWrapper;
  beforeEach(() => {
    wrapper = shallow(<AddTask />);
  });
  afterAll(() => {
    jest.resetAllMocks();
  });

  it('called handleKeyDown fn', () => {
    wrapper.find('input').simulate('keydown', { which: 13, target: { value: 'teresa teng' } });
    expect(useDispatch).toBeCalledTimes(1);
    expect(mDispatch).toBeCalledWith({ type: 'todoAdd', payload: 'teresa teng' });
  });
});

单元测试结果:

 PASS  examples/66328381/AddTask.test.tsx
  Task:
    ✓ called handleKeyDown fn (7 ms)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |     100 |       75 |     100 |     100 |                   
 AddTask.tsx |     100 |       75 |     100 |     100 | 9                 
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.766 s, estimated 4 s