【问题标题】:How do I unit test a HOC having an async componentDidMount using jest?如何使用 jest 对具有异步 componentDidMount 的 HOC 进行单元测试?
【发布时间】:2021-05-21 10:20:12
【问题描述】:

这是我的反应 HOC:

import React from "react"
import { getData } from 'services/DataService'

export function withData(WrappedComponent, count) {
  return class extends React.Component {
    state = { 
      data: [] 
    }

    async componentDidMount() {
      this.setState({ 
        data: await getData(count) 
      })
    }

    render() {
      const { data } = this.state
      return data.length > 0 && <WrappedComponent data={data} />
    }
  }
}

我想写一个单元测试来证明如果getData(count)返回一些数据,WrappedComponent被正确渲染或者至少state.data.length大于零。

我在测试中使用有效的mockResponse 像这样嘲笑getData

  jest.mock("services/DataService", () => ({
    getData: jest.fn().mockImplementation(() => Promise.resolve(mockResponse))
  }))

但我被困在这一点上。社区有一些关于测试具有异步 componentDidMount 的组件的答案,但它们都与 HOC 无关。我试过这样的事情:

const WithReleaseNotesComponent = withReleaseNotes(mockedComponent)

然后尝试实例化或挂载WithReleaseNotesComponent,但我从未见过他们有我可以测试的正确状态/道具。状态/道具总是空的。我错过了什么?

【问题讨论】:

    标签: reactjs unit-testing async-await jestjs higher-order-components


    【解决方案1】:

    我将使用enzyme 库来测试您的组件。为了保证使用async/await语法的componentDidMount生命周期方法的异步任务执行完成,我们需要刷新promise队列。

    有关刷新承诺队列如何做的更多信息,请参阅macrotasks-and-microtasks

    所有微任务在任何其他事件处理或渲染或任何其他宏任务发生之前完成。

    这意味着async/await 和promise 是微任务,它们将在setTimeout 创建的宏任务之前完成。

    例如

    index.tsx:

    import React from 'react';
    import { getData } from './services/DataService';
    
    export function withData(WrappedComponent, count) {
      return class extends React.Component {
        state = {
          data: [],
        };
    
        async componentDidMount() {
          this.setState({
            data: await getData(count),
          });
        }
    
        render() {
          const { data } = this.state;
          return data.length > 0 && <WrappedComponent data={data} />;
        }
      };
    }
    

    index.test.tsx:

    import React from 'react';
    import { withData } from './';
    import { getData } from './services/DataService';
    import { shallow } from 'enzyme';
    import { mocked } from 'ts-jest/utils';
    
    jest.mock('./services/DataService');
    
    const mockedGetData = mocked(getData);
    
    class MockedComponent extends React.Component {
      render() {
        return <div>mocked component</div>;
      }
    }
    
    function flushPromises() {
      return new Promise((resolve) => setTimeout(resolve, 0));
    }
    
    describe('66260393', () => {
      afterAll(() => {
        jest.resetAllMocks();
      });
      it('should render wrapped component', async () => {
        mockedGetData.mockResolvedValueOnce('fake data');
        const WithDataComponent = withData(MockedComponent, 1);
        const wrapper = shallow(<WithDataComponent></WithDataComponent>);
        expect(wrapper.state('data')).toEqual([]);
        await flushPromises();
        expect(wrapper.state('data')).toEqual('fake data');
        expect(wrapper.find(MockedComponent)).toBeTruthy();
        expect(wrapper.find(MockedComponent).prop('data')).toEqual('fake data');
      });
    });
    

    单元测试结果:

     PASS  examples/66260393/index.test.tsx
      66260393
        ✓ should render wrapped component (23 ms)
    
    -------------------|---------|----------|---------|---------|-------------------
    File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    -------------------|---------|----------|---------|---------|-------------------
    All files          |      90 |      100 |      80 |      90 |                   
     66260393          |     100 |      100 |     100 |     100 |                   
      index.tsx        |     100 |      100 |     100 |     100 |                   
     66260393/services |      50 |      100 |       0 |      50 |                   
      DataService.ts   |      50 |      100 |       0 |      50 | 2                 
    -------------------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        3.198 s
    

    【讨论】:

    • 我不使用那个 'ts-jest/utils' 库,所以不能完全测试你的解决方案,但是是的,问题是我的模拟。
    • @schlingel 去掉这一行,其他都一样
    【解决方案2】:

    问题是我的模拟。我不知道为什么,但jest.mock() 没有按预期工作。我用jest.spyOn() 替换它,然后用await 调用shallow。一切都很完美。甚至不需要刷新承诺。

    withDataHOC.spec.js

    import React from 'react'
    import { shallow } from 'enzyme'
    import { withData } from 'components/higher-order-components/WithDataHOC'
    import * as dataService from 'services/DataService'
    
    describe("WithDataHOC", () => {
      afterAll(() => {
        jest.resetAllMocks()
      })
    
      const mockedComponent = jest.fn()
    
      it("doesn't render anything if there are no data", async() => {    
        jest.spyOn(dataService, "getData").mockImplementation( () => Promise.resolve()) 
        const WithDataComponent = withData(mockedComponent)
        const wrapper = shallow(<WithDataComponent />)
        expect(wrapper).toMatchSnapshot()
      })
      const mockResponse = JSON.parse(require('services/__mocks__/DataResponse').dataResponseCache)
    
      it("renders correctly if there are data", async() => {     
        jest.spyOn(dataService, "getData").mockImplementation( () => Promise.resolve(mockResponse)) 
          const WithDataComponent = withData(mockedComponent)
          const wrapper = await shallow(<WithDataComponent />)            
          expect(wrapper).toMatchSnapshot()        
      })
    })
    

    【讨论】:

      猜你喜欢
      • 2018-08-31
      • 2017-04-14
      • 2020-03-15
      • 2020-01-20
      • 2018-12-23
      • 2018-07-19
      • 1970-01-01
      • 2016-03-19
      • 2021-08-08
      相关资源
      最近更新 更多