使用 Jest 和 React 模拟非默认函数

Posted

技术标签:

【中文标题】使用 Jest 和 React 模拟非默认函数【英文标题】:Mock a non-default function using Jest and React 【发布时间】:2019-05-05 06:33:40 【问题描述】:

在一个测试文件中,我需要渲染一个组件,同时模拟它的一些子组件。文件结构大致如下所示。

文件 1

import A, B from 'a-module';

export function MyComponent() 
    return (
        <div>
            <A /> // I need to mock
            <B /> // these components out
        </div>
    );

文件 2

import MyComponent from 'File 1';

/*
 * In this file I would like to render MyComponent but
 * have components A and B be replaced by mocks
 */

我尝试过jest.mock('a-module', () =&gt; 'Blah');,但这并没有成功地模拟组件。但是,当在文件 1 中使用默认导入时,此方法有效。

如果在文件 2 中渲染 MyComponent 时模拟出组件 AB,将不胜感激!

【问题讨论】:

【参考方案1】:

您可以像这样模拟非默认值:

jest.mock('a-module', () => (
  __esModule: true,
  default: () => 'Blah',
  A: () => 'Blah',
  B: () => 'Blah'
));

https://remarkablemark.org/blog/2018/06/28/jest-mock-default-named-export/

或使用 __mocks__

作为替代方案,您可以在原始模块旁边的__mocks__ 文件夹下创建一个与模块同名的文件:

a_module_folder > 
    a-module.js
    __mocks__ >
        a-module.js

并且该模拟应该只导出模拟版本:

export const A = () => 'Blah';
export const B = () => 'Blah';

然后像这样模拟:

jest.mock('a-module');

对于 node_modules,只需将 __mocks__folder 与 node_modules 放在同一级别

https://jestjs.io/docs/en/manual-mocks

【讨论】:

您对我们为什么需要设置 __esModule 有进一步的了解吗?如果没有该选项,它似乎可以工作。 我认为只有当您还需要模拟默认导出时才需要它。见github.com/facebook/jest/blob/…【参考方案2】:

测试 React 组件主要是使用 Enzyme 完成的,如果您只尝试使用 Jest 进行测试,那么您可能选择了错误的工具。我只能猜测您为什么需要模拟一个组件,但您肯定可以使用 Enzyme 来实现它。

Enzyme 浅层渲染是专门为测试 React 而创建的。 Jest 本身不能渲染组件。根据Airbnb docs的定义是:

浅渲染有助于限制自己将组件作为一个单元进行测试,并确保您的测试不会间接断言子组件的行为。

简单地说它将渲染被测组件 1 级深度,例如

// File2.js

import  MyComponent  from 'File1';
import  shallow  from 'enzyme';

describe('MyComponent', () => 
  it('should render shallowly my component', () => 
    const wrapper = shallow(<MyComponent />);
    console.log(wrapper.debug()); 
    // output:
    //   <div>
    //     <A />
    //     <B />
    //   </div>
    // Note: even if component <A /> is consisting of some markup, 
    // it won't be rendered 
  );
);

基本上你不需要模拟它的任何依赖组件,这些已经被酶shallow()模拟了

您可以做的是在将某些道具传递给&lt;MyComponent /&gt; 时进行测试,依赖组件&lt;A /&gt;&lt;B /&gt; 正在接收预期的道具。

const wrapper = shallow(<MyComponent foo=1 bar=2 />);
expect(wrapper.find('A').props().foo).toEqual(1);
expect(wrapper.find('B').props().bar).toEqual(2);

【讨论】:

我建议不要使用酶进行测试,因为它鼓励将测试与实现细节结合起来。检查反应测试库 你应该测试的是组件的输入和输出。就像你说的,你需要检查一个元素是否是一个按钮(html按钮),但你永远不需要知道里面使用了什么反应组件,有什么内部状态等等。Enzyme 提供并鼓励基于测试的方法反应组件名称(查找)并检查内部状态。这就是我所说的实现细节。测试这些的问题是对实现的更改不会改变输出而破坏测试,更糟糕的是,实际上会破坏输出但仍然通过测试的更改。 谢谢。这是一个很好的答案,如果我应该使用酶,它会让我重新思考,但它并没有直接回答我的问题。【参考方案3】:

如果你有一个不是默认导出的 react 组件,它实际上是一个good practice (since default exports should be avoided):

export function MyComponentX() 
    return (
        <div>...</div>
    );

你可以轻松地嘲笑它:

jest.mock('path/to/MyComponentX', () => (
  MyComponentX: () => <>MOCK_MY_COMPONENT_X</>,
));

【讨论】:

以上是关于使用 Jest 和 React 模拟非默认函数的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Jest 模拟对象中的特定函数?

Jest / Enzyme - 生命周期方法中的模拟异步函数

Jest + React-testing-library-等待模拟的异步功能完成

如何测试从 mapDispatchToProps 传递的函数(React/Redux/Enzyme/Jest)

Jest spyOn 函数调用

如何使用 Typescript 将 jest.spyOn 与 React 函数组件一起使用