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

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

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

最好的测试方法是什么?

【问题讨论】:

标签: reactjs mocha.js redux enzyme


【解决方案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()
          猜你喜欢
          • 2019-05-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-08
          • 2019-01-27
          • 2019-05-15
          • 1970-01-01
          • 2018-10-19
          相关资源
          最近更新 更多