0

我正在尝试测试我使用 RTK-Query 编写的一些功能。我使用 createApi 创建了一个 api 并导出了 useLazyQuery 钩子。我正在调用这个钩子,然后在 useEffect 钩子中监听结果变化。这在应用程序中按预期工作。当我尝试使用 msw 和 @testing-library/react-native 为这个逻辑编写测试时,我遇到了错误。

当我运行测试时,我看到以下控制台输出:

Jest did not exit one second after the test run has completed.

This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.

--detectOpenHandles 标志没有帮助。

我的测试如下所示:

const server = setupServer();
const mockedNavigate = jest.fn();

jest.mock('@react-navigation/native', () => {
  return {
    ...jest.requireActual('@react-navigation/native'),
    useNavigation: () => ({
      navigate: mockedNavigate,
    }),
  };
});

describe('ForgotPasswordForm', () => {
  const storeRef = setupApiStore(forgotPasswordApi);

  const testRender = () =>
    render(
      <Provider store={storeRef.store}>
        <ForgotPasswordForm />
      </Provider>
    );

  beforeAll(() => {
    jest.spyOn(Alert, 'alert');
    server.listen();
  });

  beforeEach(() => {
    storeRef.store.dispatch(forgotPasswordApi.util.resetApiState());
  });

  afterEach(() => {
    jest.resetAllMocks();
    server.resetHandlers();
    cleanup();
  });

  afterAll(() => {
    server.close();
  });

  it('should display an error alert if the email is not registered', async () => {
    server.use(
      rest.get(`${API_ENDPOINT}/ResetPassword`, (_, res, ctx) =>
        res(ctx.status(200), ctx.json({ status: 'error' }))
      )
    );

    const { getByText, getByPlaceholderText } = testRender();

    fireEvent.changeText(
      getByPlaceholderText('Registered email address'),
      'unregistered@test.com'
    );

    fireEvent.press(getByText(/Retrieve Password/i));

    await waitFor(() =>
      expect(Alert.alert).toHaveBeenCalledWith(
        'Error',
        'An error has occured. Please contact us for help.'
      )
    );
  });
});

我的 API 如下所示:

export const forgotPasswordApi = createApi({
  reducerPath: 'forgotPassword',
  baseQuery: fetchBaseQuery({
    baseUrl: API_ENDPOINT,
  }),
  endpoints: (builder) => ({
    resetPassword: builder.query({
      query: (email) => ({
        url: '/ResetPassword',
        params: { email },
      }),
    }),
  }),
});

export const { useLazyResetPasswordQuery } = forgotPasswordApi;

我的组件如下所示:

const ForgotPasswordForm = () => {
  const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();

  const [email, setEmail] = useState('');
  const [showInvalidEmailMessage, setShowInvalidEmailMessage] = useState(false);

  const handleEmailChange = (value: string) => {
    setEmail(value);
  };

  const [triggerResetPasswordQuery, results] = useLazyResetPasswordQuery();

  useEffect(() => {
    if (results.isUninitialized || results.isFetching) return;

    if (results?.data?.status === 'error') {
      Alert.alert('Error', 'An error has occured. Please contact us for help.');
    } else {
      Alert.alert('An email has been sent with further instructions.');
    }
  }, [results]);

  const handleForgotPassword = () => {
    const isEmailFormatValid = /^\S+@\S+\.\S+$/.test(email);

    if (isEmailFormatValid) {
      setShowInvalidEmailMessage(false);
      triggerResetPasswordQuery(email);
    } else {
      setShowInvalidEmailMessage(true);
    }
  };

  return (
    <>
      <Wrapper width="100%" mt={40} mb={20}>
        <TextInput
          value={email}
          placeholder="Registered email address"
          handleOnChangeText={handleEmailChange}
          accessibilityLabel="forgot-password-email"
        />
      </Wrapper>
      <Wrapper mb={10} width="100%">
        <Button
          fullWidth
          title="Retrieve Password"
          onPress={handleForgotPassword}
        />
      </Wrapper>
      {results.isLoading && <LoadingOverlay text="Sending Email" />}
    </>
  );
};

export default ForgotPasswordForm;

谢谢。

4

1 回答 1

1

我遇到了类似的问题,但就我而言,我没有使用 RTK Query 生成的钩子的惰性版本。当代码在测试环境中运行时,我通过阻止 API 保留数据来修复它。我的解决方案的代码示例:

const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({
    baseUrl: 'any-base-url',
  }),
  keepUnusedDataFor: process.env.NODE_ENV !== 'test' ? 60 : 0, // <- here
  endpoints: () => {
    return {
      // your endpoints
    };
  },
});

参考keepUnusedDataForhttps ://redux-toolkit.js.org/rtk-query/api/createApi#keepunuseddatafor

于 2022-01-16T21:28:57.530 回答