使用 React 测试库模拟上下文提供程序时出现“TypeError:react.useContext 不是函数”

Posted

技术标签:

【中文标题】使用 React 测试库模拟上下文提供程序时出现“TypeError:react.useContext 不是函数”【英文标题】:"TypeError: react.useContext is not a function" when mocking context Providers with React Testing Library 【发布时间】:2021-01-12 19:49:41 【问题描述】:

我正在尝试确定在使用 React Context 进行 Jest 测试时如何最好地模拟提供程序。我发现的最有前途的线索是 RTL,它在他们的文档中提供了以下示例:https://testing-library.com/docs/example-react-context

我一直在尝试多种结构,但即使我密切关注他们的示例,我也会遇到上述错误。

在测试我的 Dialog 组件时,我几乎完全按照他们的示例进行操作:

笑话测试

import React from 'react';
import  render  from '@testing-library/react';
import  DialogContext, defaultState  from '../../../../store/contexts/dialogContext';

const customRender = (ui,  providerProps, ...renderOptions ) => 
  return render(
    <DialogContext.Provider ...providerProps>ui</DialogContext.Provider>,
    renderOptions
  );
;

it('Dialog should work', async () => 
  const providerProps = 
    dialogType: 'CANCEL',
    ...defaultState,
  ;
  const  container  = customRender(<Dialog />,  providerProps );
  const results = await axe(container);

  expect(results).toHaveNoViolations();
);

对话框组件

import React,  useContext  from 'react';
import  DialogContext  from '../../../store/contexts/dialogContext';

export default function Dialog() 
  const [dialogState, dialogDispatch] = useContext(DialogContext);

  //logic

 return (
  <div id='dialog'/>
  );

这是我的对话框组件的简化版本,但在第 2 行调用 useContext() 时我收到以下错误。

Error: Uncaught [TypeError: (0 , _react.useContext) is not a function or its return value is not iterable]

由于“React”在相关文件的范围内,而且我正在密切关注 RTL 示例,所以我很难过。欢迎提供其他模拟提供商方法的解决方案或建议。

编辑: 为清楚起见,添加 dialogContext 文件。当不在 Jest 流中时,组件和上下文可以正常工作。

import React,  createContext, useReducer  from 'react';
import  dialogReducer  from '../reducers/dialogReducer';

export const DialogContext = createContext();
const  Provider  = DialogContext;

export const defaultState = 
  //state object


export const DialogProvider = ( children ) => 
  const [state, reducer] = useReducer(dialogReducer, defaultState);

  return <Provider value=[state, reducer]>children</Provider>

当 DialogContext 从 Dialog 组件(前面的行)中记录时,它看起来与预期一样:

 '$$typeof': Symbol(react.context),
        _calculateChangedBits: null,
        _currentValue: undefined,
        _currentValue2: ,
        _threadCount: 0,
        Provider:  '$$typeof': Symbol(react.provider), _context: [Circular] ,
        Consumer:
          '$$typeof': Symbol(react.context),
           _context: [Circular],
           _calculateChangedBits: null ,
        _currentRenderer: ,
        _currentRenderer2: null 

【问题讨论】:

useContext 是否工作非常特定于您的 Jest 配置,错误是模棱两可的,但更可能的是 useContext 在解构假定时不返回数组。您发布的代码中没有任何内容表明为上下文提供了数组值。 你能给我更多关于 Jest 配置的信息吗?我们用 CRA 设置了一个通用的 Jest,但我可以更改或覆盖它。如果不是很明显,组件和上下文在不在此模拟流程中时可以工作,但为了清楚起见,我将添加上下文文件。 如果您使用 CRA,那么 Jest 配置应该没有问题,至少在这方面是这样。谢谢。是测试代码和生产代码之间的差异导致了这里的问题。 【参考方案1】:

useContext 未定义并且useContext 是一个函数但不返回可迭代(数组)时,确实会发生错误is ambiguous。

如果 Jest 配置不正确并且 react 包被导入为 CommonJS 模块,该模块通过模块互操作转换为 ES 模块 default 导入,则可能会发生第一种情况。

第二种情况是对错误最简单的解释。上下文提供程序未提供数组值,因此 useContext 不返回数组。除非defaultState 对象包含value 属性和数组值,否则...defaultState 传播将导致错误的提供者值。

在测试中提供的值应与在应用中提供的方式相同:

<Provider value=[state, reducer]>...

如果打算在测试中不使用dialogReducer,则可以提供一个间谍:

<DialogContext.Provider value=[defaultState, mockedDispach]>...

否则可以使用DialogProvider 代替DialogContext.Provider,因为它已经提供了预期的提供者价值。

【讨论】:

您说的完全正确,谢谢!当我将原始 reducer 传递给模拟的 Provider &lt;DialogContext.Provider value=[testState, dialogReducer]&gt;ui&lt;/DialogContext.Provider&gt; 并修复了当我将 state 传递给 customRender customRender(&lt;Dialog /&gt;, testState ) 时的语法,它工作得很好----谢谢!!【参考方案2】:
jest.mock('react', () => (
    ...jest.requireActual('react'),
    useContext: jest.fn(),
))

在配置适配器之前添加这一行。

configure(adapter: new Adapter());

【讨论】:

以上是关于使用 React 测试库模拟上下文提供程序时出现“TypeError:react.useContext 不是函数”的主要内容,如果未能解决你的问题,请参考以下文章

在 React Native 中使用 axios 时出现网络错误

在 react native 中使用 jest 和 Storybook 快照测试时出现问题

尝试在模拟器或设备上构建 React Native 项目时出现 Xcode 错误

在 android 2.3 上使用 jcifs 库时出现 NoClassDefFound 异常

创建新的 React 应用程序时出现错误

运行 react-native ios 模拟器时出现间歇性错误(“由于模拟器已经启动,无法启动您定义的模拟器”)