0

卸载组件时不调用测试集状态

我有一个自定义钩子,它在页面加载时调用一个承诺来设置数据。为了确保当我使用取消请求的组件卸载时不会调用数据/错误的设置状态,如下所示:


function useService() {
    const [data, setData] = useState([]);
    const [error, setError] = useState("");
    const [loading, setLoading] = useState({});
    const [cancelRequest, setCancelRequest] = useState(false);

    const getMessgaes = async () => {
        setLoading(true);
        try {
            const res = await getChatLog();
            if (!cancelRequest) {
                setData(res);
                setLoading(false);
            }
        } catch (e) {
            if (!cancelRequest) {
                setError(e);
                setLoading(false);
            }
        }

    };

    useEffect(() => {
        getMessgaes();
        return () => {
            setCancelRequest(true);
        };
    }, []);


    return {
        data, error, loading, cancelRequest
    }
}

我的测试是:

   it("if component is unmounted before response then set data is not called", async () => {
        getChatLog.mockImplementation(() => {
            setTimeout(()=>{
                Promise.reject("error");
            },500);
        });
        const {result, unmount, waitForNextUpdate} = renderHook(() => useService());
        expect(result.current.cancelRequest).toEqual(false);
        unmount();
        expect(result.current.cancelRequest).toEqual(true);
        await waitForNextUpdate();
        expect(getChatLog).toHaveBeenCalledTimes(3);
        expect(result.current.error).toEqual("");
    });

但我收到错误:


    Warning: An update to TestHook inside a test was not wrapped in act(...).

    When testing, code that causes React state updates should be wrapped into act(...):

    act(() => {
      /* fire events that update state */
    });
    /* assert on the output */


    Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
        in TestHook
        in Suspense

有人可以指导如何测试吗?

谢谢

4

1 回答 1

0

首先,永远不要调用hooksetState()的清理函数,和componentWillUnmount()一样。useEffect

不应该调用 setState()因为componentWillUnmount()组件永远不会被重新渲染。一旦组件实例被卸载,它将永远不会再次被安装。

因此,您对被测代码的清理useEffect没有意义。

其次,setTimeout()在模拟实现中使用延迟也没有意义,我们不需要在测试用例中延迟它,它会使测试用例变慢,我们必须为它模拟计时器。你可以mock.mockRejectedValueOnce('error')改用。

最后,你应该await waitForNextUpdate()在断言之前解决警告:Warning: An update to TestComponent inside a test was not wrapped in act(...)..

例如

useService.ts

import { useEffect, useState } from 'react';
import { getChatLog } from './getChatLog';

export function useService() {
  const [data, setData] = useState<string[]>([]);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState({});
  const [cancelRequest, setCancelRequest] = useState(false);

  const getMessgaes = async () => {
    setLoading(true);
    try {
      const res = await getChatLog();
      if (!cancelRequest) {
        setData(res);
        setLoading(false);
      }
    } catch (e) {
      if (!cancelRequest) {
        setError(e);
        setLoading(false);
      }
    }
  };

  useEffect(() => {
    getMessgaes();
  }, []);

  return { data, error, loading, cancelRequest };
}

getChatLog.ts

export async function getChatLog() {
  return ['real data'];
}

useService.test.ts

import { renderHook } from '@testing-library/react-hooks';
import { useService } from './useService';
import { getChatLog } from './getChatLog';
import { mocked } from 'ts-jest/utils';

jest.mock('./getChatLog');

describe('58026328', () => {
  test('should pass', async () => {
    const mGetChatLog = mocked(getChatLog);
    mGetChatLog.mockRejectedValueOnce('error');
    const { result, waitForNextUpdate } = renderHook(useService);
    await waitForNextUpdate();
    expect(result.current.cancelRequest).toEqual(false);
  });
});

测试结果:

 PASS  examples/58026328/useService.test.ts
  58026328
    ✓ should pass (20 ms)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |   82.61 |       25 |      80 |   81.82 |                   
 getChatLog.ts |      50 |      100 |       0 |      50 | 2                 
 useService.ts |   85.71 |       25 |     100 |      85 | 14-16             
---------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.56 s, estimated 13 s
于 2022-01-04T07:30:29.613 回答