在 React 和 Redux 中使用 Enzyme 进行嵌套组件测试

Posted

技术标签:

【中文标题】在 React 和 Redux 中使用 Enzyme 进行嵌套组件测试【英文标题】:Nested components testing with Enzyme inside of React & Redux 【发布时间】:2016-10-14 09:39:47 【问题描述】:

我有一个组件SampleComponent,它安装了另一个“连接组件”(即container)。当我尝试通过mounting 测试SampleComponent 时(因为我需要componentDidMount),我得到了错误:

不变违规:在上下文中找不到“商店”或 “连接(容器组件)”的道具。要么包装根组件 在 a 中,或明确地将“store”作为道具传递给 “连接(容器组件)”。

最好的测试方法是什么?

【问题讨论】:

从 Enzyme v3 开始,浅调用 ComponentDidMount github.com/airbnb/enzyme/blob/master/docs/api/shallow.md FYI 【参考方案1】:

还有使用redux-mock-store的选项。

用于测试 Redux 异步操作创建者和中间件的模拟商店。模拟存储将创建一个调度的动作数组,作为测试的动作日志。

mock store 在 store 对象上提供了 Redux 所需的必要方法。 您可以指定可选的中间件和您的应用特定的初始状态。

import configureStore from 'redux-mock-store'

const middlewares = []
const mockStore = configureStore(middlewares)

const initialState = 
const store = mockStore(initialState)

const wrapper = mount(<SampleComponent store=store/>)

【讨论】:

【参考方案2】:

为了使装饰器语法的使用更具可测试性,我做了这个: https://www.npmjs.com/package/babel-plugin-undecorate

输入:

@anyOldClassDecorator
export class AnyOldClass 
  @anyOldMethodDecorator
  method() 
    console.log('hello');   
  

输出:

@anyOldClassDecorator
export class AnyOldClass 
  @anyOldMethodDecorator
  method() 
    console.log('hello');   
  


export class __undecorated__AnyOldClass 
  method() 
    console.log('hello');   
  

希望这可以提供一个可靠的选项 3

【讨论】:

【参考方案3】:

选项 1)

您可以在测试中使用 React-Redux 的 Provider 组件包装容器组件。因此,使用这种方法,您实际上引用了 store,将其传递给 Provider,并在内部编写您的测试组件。这种方法的优点是您实际上可以为测试创建自定义存储。如果您想测试组件中与 Redux 相关的部分,这种方法很有用。

选项 2)

也许你不关心测试与 Redux 相关的部分。如果您只是对测试组件的渲染和本地状态相关的行为感兴趣,您可以简单地为组件的未连接纯版本添加一个命名导出。并且只是为了澄清当您将“export”关键字添加到您的类时,您基本上是在说现在可以通过两种方式导入该类,或者使用大括号 或不使用。示例:

export class MyComponent extends React.Component render() ... 

...

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

稍后在您的测试文件中:

import MyComponent from 'your-path/MyComponent'; // it needs a store because you use "default export" with connect
import MyComponent from 'your-path/MyComponent'; // don't need store because you use "export" on top of your class.

希望对大家有所帮助。

【讨论】:

【参考方案4】:

您可以使用名称导出来解决这个问题:

你应该有:

class SampleComponent extends React.Component
...
   render()
       <div></div>
   


export default connect(mapStateToProps, mapDispatchToProps)(SampleComponent)

您可以在课前添加导出:

export class SampleComponent extends React.Component

并在没有 redux 存储的情况下导入此组件:

import  SampleComponent  from 'your-path/SampleComponent';

使用此解决方案,您无需将商店导入测试文件。

【讨论】:

【参考方案5】:

Enzyme 的装载采用可选参数。您需要的两个是必要的

options.context: (Object [optional]): Context to be passed into the component

options.childContextTypes: (Object [optional]): Merged contextTypes for all children of the wrapper 您可以使用这样的选项对象挂载SampleComponent

const store =  
  subscribe: () => ,
  dispatch: () => ,
  getState: () => ( ... whatever state you need to pass in ... )

const options = 
  context:  store , 
  childContextTypes:  store: React.PropTypes.object.isRequired  


const _wrapper = mount(<SampleComponent ...defaultProps />, options)

现在您的 SampleComponent 会将您提供的上下文传递给 connected component

【讨论】:

这太完美了!虽然公认的答案在大多数情况下都有效,但缺点是您无法充分利用 mount api。例如,使用将组件包装在Provider 中的公认答案将不允许使用wrapper.state() api。该解决方案将为您的包装器提供全方位的方法。 这是一个比接受的答案更好的答案,原因如上所述(即您安装的包装器实际上不是您要测试的组件),并且还因为您可以使用模拟商店而不是您的实际商店,将所有 redux 排除在外。 文档中没有这个可选参数,你是怎么找到的?在代码中? 如果SampleComponent 使用一个名为SubComponent 的连接组件,并且我们想检查它是否使用mount 正确呈现(集成测试,而不是单元)?您可以将存储作为第二个参数传递给SampleComponent,但SubComponent 将没有存储。【参考方案6】:

我所做的基本上是引入我的redux 存储(和Provider)并将其包装在一个实用程序组件中,如下所示:

export const CustomProvider = ( children ) => 
  return (
    <Provider store=store>
      children
    </Provider>
  );
;

然后,我 mount SampleComponent 并针对它运行测试:

it('contains <ChildComponent/> Component', () => 
  const wrapper = mount(
    <CustomProvider>
      <SampleComponent ...defaultProps />
    </CustomProvider>
  );
  expect(wrapper.find(ChildComponent)).to.have.length(1);
);

【讨论】:

我看到你正在使用 mount,如果我尝试用 mount 替换 shallo 我会收到错误消息。你也遇到过吗? 虽然此答案在某些情况下有效,但当您需要测试组件的生命周期时它不起作用。例如,调用wrapper.setProps() 不会在SampleComponent 上触发componentWillReceiveProps()

以上是关于在 React 和 Redux 中使用 Enzyme 进行嵌套组件测试的主要内容,如果未能解决你的问题,请参考以下文章

React Native 已经有了异步存储。为啥我应该在我的 react native 应用中使用 Redux 和 Redux Thunk?

redux和react-redux

使用 connect 与 react-redux 和 redux-persist

react中数据管理:react-redux和redux的使用

React-redux: React.js 和 Redux 架构的结合

Redux 使用 react-router-redux 连接“块”导航