reactjs - React Hooks - 如何测试全局提供者的更改

标签 reactjs react-hooks react-testing-library react-hooks-testing-library

我正在尝试测试以下场景:

  • 拥有过期 token 的用户尝试访问未经授权的资源
  • 资源返回 401 错误
  • 应用程序将全局状态“isExpiredSession”更新为 true

为此,我有 2 个提供商:

  • 身份验证提供程序,具有全局身份验证状态
  • 负责获取资源的人

两者都有自定义 Hook ,公开这些组件的共享逻辑,即:fetchResource/expireSession

当获取的资源返回 401 状态时,它会通过共享 setState 方法在身份验证提供程序中设置 isExpiredSession 值。

AuthenticationContext.js 从 'react' 导入 React, { createContext, useState };

const AuthenticationContext = createContext([{}, () => {}]);

const initialState = {
  userInfo: null,
  errorMessage: null,
  isExpiredSession: false,
};

const AuthenticationProvider = ({ authStateTest, children }) => {
  const [authState, setAuthState] = useState(initialState);

  return (
    <AuthenticationContext.Provider value={[authStateTest || authState, setAuthState]}>
      { children }
    </AuthenticationContext.Provider>);
};


export { AuthenticationContext, AuthenticationProvider, initialState };

useAuthentication.js

import { AuthenticationContext, initialState } from './AuthenticationContext';


const useAuthentication = () => {
  const [authState, setAuthState] = useContext(AuthenticationContext);
  ...
  const expireSession = () => {
    setAuthState({
      ...authState,
      isExpiredSession: true,
    });
  };
  ...
  return { expireSession };
 }

ResourceContext.js 和认证类似,暴露一个Provider

useResource.js 有这样的内容:

const useResource = () => {
  const [resourceState, setResourceState] = useContext(ResourceContext);
  const [authState, setAuthState] = useContext(AuthenticationContext);

  const { expireSession } = useAuthentication();

  const getResource = () => {
    const { values } = resourceState;
    const { userInfo } = authState;

    return MyService.fetchResource(userInfo.token)
      .then((result) => {
        if (result.ok) {
          result.json()
            .then((json) => {
              setResourceState({
                ...resourceState,
                values: json,
              });
            })
            .catch((error) => {
              setErrorMessage(`Error decoding response: ${error.message}`);
            });
        } else {
          const errorMessage = result.status === 401 ?
            'Your session is expired, please login again' :
            'Error retrieving earnings';
          setErrorMessage(errorMessage);
          expireSession();

        }
      })
      .catch((error) => {
        setErrorMessage(error.message);
      });
  };
  ...

然后,在我的测试中,使用react-hooks-testing-library我执行以下操作:

  it.only('Should fail to get resource with invalid session', async () => {
    const wrapper = ({ children }) => (
      <AuthenticationProvider authStateTest={{ userInfo: { token: 'FOOBAR' }, isExpiredSession: false }}>
        <ResourceProvider>{children}</ResourceProvider>
      </AuthenticationProvider>
    );
    const { result, waitForNextUpdate } = renderHook(() => useResource(), { wrapper });

    fetch.mockResponse(JSON.stringify({}), { status: 401 });

    act(() => result.current.getResource());
    await waitForNextUpdate();

    expect(result.current.errorMessage).toEqual('Your session is expired, please login again');
    // Here is the issue, how to test the global value of the Authentication context? the line below, of course, doesn't work
    expect(result.current.isExpiredSession).toBeTruthy();
  });

我尝试了一些解决方案:

  • 也在测试中渲染 useAuthentication,但是,资源所做的更改似乎并未反射(reflect)在它上。
  • 通过 Resource hook 公开 isExpiredSession 变量,即:
      return { 
            ...
            isExpiredSession: authState.isExpiredSession,
            ...
       };

我预计到那时这条线会起作用:

expect(result.current.isExpiredSession).toBeTruthy();

但仍然无法正常工作,并且该值仍然为 false

知道如何解决这个问题吗?

最佳答案

这里是react-hooks-testing-library的作者。

无法运行代码有点困难,但我认为您的问题可能是多个状态更新无法正确批处理,因为它们没有包含在act中> 打电话。 an alpha release of react (v16.9.0-alpha.0) 对异步调用进行操作的能力我们有 an issue tracking it也是如此。

所以可能有2种方法可以解决:

  1. 更新到 alpha 版本并将 waitForNextUpdate 移至 act 回调
npm install react@16.9.0-alpha.0
  it.only('Should fail to get resource with invalid session', async () => {
    const wrapper = ({ children }) => (
      <AuthenticationProvider authStateTest={{ userInfo: { token: 'FOOBAR' }, isExpiredSession: false }}>
        <ResourceProvider>{children}</ResourceProvider>
      </AuthenticationProvider>
    );
    const { result, waitForNextUpdate } = renderHook(() => useResource(), { wrapper });

    fetch.mockResponse(JSON.stringify({}), { status: 401 });

    await act(async () => {
      result.current.getResource();
      await waitForNextUpdate();
    });

    expect(result.current.errorMessage).toEqual('Your session is expired, please login again');

    expect(result.current.isExpiredSession).toBeTruthy();
  });
  • 添加第二个 waitForNextUpdate 调用
  •   it.only('Should fail to get resource with invalid session', async () => {
        const wrapper = ({ children }) => (
          <AuthenticationProvider authStateTest={{ userInfo: { token: 'FOOBAR' }, isExpiredSession: false }}>
            <ResourceProvider>{children}</ResourceProvider>
          </AuthenticationProvider>
        );
        const { result, waitForNextUpdate } = renderHook(() => useResource(), { wrapper });
    
        fetch.mockResponse(JSON.stringify({}), { status: 401 });
    
        act(() => result.current.getResource());
    
        // await setErrorMessage to happen
        await waitForNextUpdate();
    
        // await setAuthState to happen
        await waitForNextUpdate();
    
        expect(result.current.errorMessage).toEqual('Your session is expired, please login again');
    
        expect(result.current.isExpiredSession).toBeTruthy();
      });
    

    您对使用 alpha 版本的兴趣可能会决定您选择哪个选项,但是,选项 1 更“面向 future ”。一旦 alpha 版本发布稳定版本,选项 2 可能有一天会停止工作。

    关于reactjs - React Hooks - 如何测试全局提供者的更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56427502/

    相关文章:

    javascript - 使用 onChange 更新组件。 React-Hooks

    reactjs - 如何模拟 Firebase Auth 方法? ( react ,测试库)

    reactjs - 使用 React 测试库测试登录功能

    javascript - 我对 React 和 TypeScript 中的这个简单组件感到困惑

    html - 我应该如何设计这个 img 元素来达到我想要的效果

    javascript - 与 React Native 开 Jest 得到问题

    reactjs - React Context API 很慢

    javascript - 样式化组件 onClick 旋转元素

    reactjs - Next.js + React 返回上一页

    reactjs - 如何在 React 中使用 Enzyme 或 React 测试库测试 Material-UI 的响应式 UI(例如隐藏、网格、断点)