react-native - 在 React Native 中使用 RTK-Query useLazyQuery 时,Jest 无法正确退出

标签 react-native redux jestjs redux-toolkit rtk-query

我正在尝试测试我使用 RTK-Query 编写的一些功能。 我使用 createApi 创建了一个 api 并导出了 useLazyQuery Hook 。 我正在调用此 Hook ,然后监听 useEffect Hook 中的结果更改。 这按应用程序中的预期工作。 当我尝试使用 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'),
      '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f2879c8097959b818697809796b286978186dc919d9f" rel="noreferrer noopener nofollow">[email protected]</a>'
    );

    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;

谢谢。

最佳答案

我遇到了类似的问题,但就我而言,我没有使用 RTK 查询生成的 Hook 的惰性版本。我通过阻止 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
    };
  },
});

引用keepUnusedDataFor:https://redux-toolkit.js.org/rtk-query/api/createApi#keepunuseddatafor

关于react-native - 在 React Native 中使用 RTK-Query useLazyQuery 时,Jest 无法正确退出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69538390/

相关文章:

reactjs - 如何从 strip 付款表中获取 "Save Card Details"选项?

oauth-2.0 - React Native 应用程序 - 如何使用 oauth 2.0 和 Openid 连接?

javascript - TypeScript 装饰器中的异步调用

javascript - 开 Jest 测试: Awating multiple promises in connected component

javascript - 如何查看苹果收据的有效期?

react-native - react native 路由器通量和 native 基础容器

react-native - React-Native 中的指针事件是什么

javascript - react Redux 未捕获错误 : Expected the reducer to be a function

javascript - redux thunk 中链接操作的正确方法?

javascript - 哪个导入与 FontAwesomeIcon、React 和 Jest 一起使用?