开发者问题收集

如何在 useEffect 中测试异步函数的基本示例

2019-12-05
556

我有一个简单的组件,它可以获取帖子的异步列表。

export const Posts = () => {
  const [list, dispatch] = useReducer(listReducer, []);

  useEffect(() => {
    fetchList(dispatch);
  }, []);

  return (
    <ul>
      {list.map((el) => (
        <li key={el.id}>{el.title}</li>
      ))}
    </ul>
  );
};

在其他文件中,我保留了以下逻辑:

export const fetchList = async (dispatch) => {
  try {
    const result = await api.get('/list/') /* AXIOS */
    dispatch({ type: LIST_SUCCES, payload: result.data.list })
  } catch (error) {
    dispatch({ type: LIST_FAILURE })
  }
}

export const listReducer = (state, action) => {
  switch (action.type) {
    case LIST_SUCCES: 
      return action.payload
    case LIST_FAILURE:
      return []
    default:
      throw new Error()
  }
}

我尝试了多个库,但就是无法编写测试。我该如何编写 Posts.test.js 来检查帖子是否已获取并显示,我在第一次安装组件后触发异步 fetchList (因此它不是 componentDidMount ),并且在获取数据后,我从该异步函数分派操作并更新列表。

1个回答

这是单元测试解决方案:

index.tsx

import React, { useReducer, useEffect } from 'react';
import { listReducer, fetchList } from './reducer';

export const Posts = () => {
  const [list, dispatch] = useReducer(listReducer, []);

  useEffect(() => {
    fetchList(dispatch);
  }, []);

  return (
    <ul>
      {list.map((el) => (
        <li key={el.id}>{el.title}</li>
      ))}
    </ul>
  );
};

reducer.ts

import axios from 'axios';
const LIST_SUCCES = 'LIST_SUCCES';
const LIST_FAILURE = 'LIST_FAILURE';

export const fetchList = async (dispatch) => {
  try {
    const result = await axios.get('/list/'); /* AXIOS */
    dispatch({ type: LIST_SUCCES, payload: result.data.list });
  } catch (error) {
    dispatch({ type: LIST_FAILURE });
  }
};

export const listReducer = (state, action) => {
  switch (action.type) {
    case LIST_SUCCES:
      return action.payload;
    case LIST_FAILURE:
      return [];
    default:
      throw new Error();
  }
};

index.spec.tsx

import React from 'react';
import { Posts } from './';
import { mount } from 'enzyme';
import axios from 'axios';
import { act } from 'react-dom/test-utils';

describe('Posts', () => {
  afterAll(() => {
    jest.restoreAllMocks();
  });
  it('should render list correctly', async () => {
    const mResponse = { data: { list: [{ id: 1, title: 'jest' }] } };
    jest.spyOn(axios, 'get').mockResolvedValueOnce(mResponse);
    const wrapper = mount(<Posts></Posts>);
    expect(wrapper.find('ul').children()).toHaveLength(0);
    await act(async () => {
      await new Promise((resolve) => setTimeout(resolve, 0));
    });
    wrapper.update();
    expect(wrapper.find('ul').children()).toHaveLength(1);
    expect(wrapper).toMatchInlineSnapshot(`
      <Component>
        <ul>
          <li
            key="1"
          >
            jest
          </li>
        </ul>
      </Component>
    `);
  });

  it('should render empty list when request list data failed', async () => {
    const mError = new Error('Internal server error');
    jest.spyOn(axios, 'get').mockRejectedValueOnce(mError);
    const wrapper = mount(<Posts></Posts>);
    expect(wrapper.find('ul').children()).toHaveLength(0);
    await act(async () => {
      await new Promise((resolve) => setTimeout(resolve, 0));
    });
    wrapper.update();
    expect(wrapper.find('ul').children()).toHaveLength(0);
    expect(wrapper).toMatchInlineSnapshot(`
      <Component>
        <ul />
      </Component>
    `);
  });
});

带有覆盖率报告的单元测试结果:

PASS  src/stackoverflow/59197574/index.spec.tsx (12.494s)
  Posts
    ✓ should render list correctly (105ms)
    ✓ should render empty list when request list data failed (37ms)

 › 1 snapshot written.
------------|----------|----------|----------|----------|-------------------|
File        |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
------------|----------|----------|----------|----------|-------------------|
All files   |    95.83 |    66.67 |      100 |       95 |                   |
 index.tsx  |      100 |      100 |      100 |      100 |                   |
 reducer.ts |    92.86 |    66.67 |      100 |    91.67 |                21 |
------------|----------|----------|----------|----------|-------------------|
Snapshot Summary
 › 1 snapshot written from 1 test suite.

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   1 written, 1 passed, 2 total
Time:        14.409s

源代码: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59197574

Lin Du
2019-12-06