【问题标题】:Testing a nested async call react component测试嵌套的异步调用反应组件
【发布时间】:2020-10-22 13:52:23
【问题描述】:

我有以下组件,其中包含一个用 typescript 编写的嵌套异步调用的反应钩子。

const sampleComponent: FunctionComponent = (): ReactElement => {
    const [ detail, setDetail ] = useState<detailObject>(undefined);

    useEffect(() => {
        getConfigs();
    }, [ detail != undefined ]);

    const getConfigs = () => {
        getConfig().then((configResp: any) => {
            if(response.status === 200) {
                getConfigDetail(configResp.data.id).then((response) => {
                    if(response.status === 200) {
                        setDetail(response.data)
                    }
                })
            }
        })
    }

    return (
        //Some UI related to the responses.
    )
}

我用以下测试对此进行了测试。

const configList = jest.spyOn(api, "getConfigList");
const configDetail = jest.spyOn(api, "getConfig");

configList.mockImplementation(() => {
    return Promise.resolve(configListRequestResponse); // these are sample responses
});

configDetail.mockImplementation(() => {
    return Promise.resolve(configDetailsRequestResponse); // these are sample responses
})

test("Test proper rendering config component", async () => {
   await act(async () => {
      render(
          <Provider store={ store }>
              <sampleComponent/>
          </Provider>
      );
      await waitFor(() => expect(configList).toHaveBeenCalledTimes(1));
      await waitFor(() => expect(configDetail).toHaveBeenCalledTimes(1));
      expect(screen.getByTestId("remote-configs")).toBeInTheDocument();
   });
});

当我运行此测试时,我收到以下错误。

警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。

    > 95 |       setDetail(response.data);
         |       ^

我在这里做错了什么?

【问题讨论】:

    标签: reactjs jestjs react-testing-library


    【解决方案1】:

    您应该创建一个 renderWithState 函数,在其中将提供程序定义为像这样的包装器

    import React, { FC, ReactElement } from 'react';
    import { Provider } from 'react-redux';
    import { render } from '@testing-library/react';
    import { createTestStore } from '../redux';
    import { AppStore } from '../redux/types';
    import { createStore } from 'redux';
    import { rootReducer } from './reducers';
    const createTestStore = (store: AppStore) => createStore(rootReducer, store);
    
    export const renderWithState = (Component: ReactElement, options?: { state: AppStore }) => {
      const store = createTestStore({dummystate:"hello"});
      const Wrapper: FC = ({ children }) => <Provider store={store}>{children}</Provider>;
    
      return render(Component, { wrapper: Wrapper });
    };
    
    describe('index tsx', () => {
      it('it should render mocked state provider', () => {
        const Hello = () => <div>hello world</div>;
        renderWithState(<Hello />, { state: initStore });
      });
    });
    

    还要测试异步行为,如下所示

      it('it renders text dummy dummies on click', async () => {
        const { queryByRole, getByText } = renderWithState(
          <Router>
            <CustomDropDown/>
          </Router>,
          {
            state: sb.store,
          },
        );
    
        const toggler = queryByRole('dropdown-toggle');
    
        await act(async () => {
          fireEvent.click(toggler!);
          await waitForElement(() => getByText('dummy'));
          await waitForElement(() => getByText('dummies'));
        });
      });
    

    希望这对您有所帮助

    【讨论】:

      猜你喜欢
      • 2017-07-19
      • 2019-11-27
      • 1970-01-01
      • 2021-10-13
      • 2017-05-20
      • 2019-01-18
      • 1970-01-01
      • 2019-01-13
      • 1970-01-01
      相关资源
      最近更新 更多