测试嵌套的异步调用react组件

时间:2020-10-22 13:52:23

标签: reactjs jestjs react-testing-library

我有一个带有react钩子的以下组件,该钩子包含用打字稿编写的嵌套异步调用。

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);
         |       ^

我在做什么错了?

1 个答案:

答案 0 :(得分:0)

您应该创建一个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'));
    });
  });

希望这对您有所帮助