首页 > 解决方案 > React 上下文测试 - 在 HOC 中模拟消费者

问题描述

我正在尝试测试我的组件,该组件通过 HOC 从上下文中消耗数据。

这是设置:模拟上下文模块/context/__mocks__

const context = { navOpen: false, toggleNav: jest.fn() } 

export const AppContext = ({
    Consumer(props) {
        return props.children(context)
    }
})

高阶组件/context/withAppContext

import React from 'react'
import { AppContext } from './AppContext.js'

/**
 * HOC with Context Consumer
 * @param {Component} Component 
 */
const withAppContext = (Component) => (props) => (
    <AppContext.Consumer>
        {state => <Component {...props} {...state}/>}
    </AppContext.Consumer>
) 

export default withAppContext

零件NavToggle

import React from 'react'
import withAppContext from '../../../context/withAppContext'

import css from './navToggle/navToggle.scss'

const NavToggle = ({ toggleNav, navOpen }) => (
    <div className={[css.navBtn, navOpen ? css.active : null].join(' ')} onClick={toggleNav}>
        <span />
        <span />
        <span />
    </div>
)

export default withAppContext(NavToggle)

最后是测试套件/navToggle/navToggle.test

import React from 'react'
import { mount } from 'enzyme'

beforeEach(() => {
  jest.resetModules()
}) 

jest.mock('../../../../context/AppContext')


describe('<NavToggle/>', () => { 
  it('Matches snapshot with default context', () => {    
        const NavToggle = require('../NavToggle')        
        const component = mount( <NavToggle/> )
        expect(component).toMatchSnapshot()
  })
})

测试只是为了开始,但我面临这个错误: Warning: Failed prop type: Component must be a valid element type! in WrapperComponent 我认为这是 HOC 的问题,我应该以某种方式而不是 AppContext 模拟它,因为从技术上讲,AppContext 不是由 NavToggle 组件直接调用,而是在包装组件中调用。

提前感谢您的任何意见。

标签: javascriptreactjsjestjsenzyme

解决方案


所以我解决了。

我的上述尝试几乎没有问题。

  1. require 不理解默认导出,除非您指定它
  2. 安装空白组件返回错误
  3. __mock__当我想修改上下文进行测试时,用文件模拟 AppContext会导致问题

我已经按照以下方式解决了。我使用自定义上下文作为参数创建了模拟 AppContext 的辅助函数

export const defaultContext = { navOpen: false, toggleNav: jest.fn(), closeNav: jest.fn(), path: '/' } 

const setMockAppContext = (context = defaultContext) => {
    return jest.doMock('../context/AppContext', () => ({
        AppContext: {
            Consumer: (props) => props.children(context)
        }
    }))  
}
export default setMockAppContext

然后测试文件结束看起来像这样

import React from 'react'
import { shallow } from 'enzyme'
import NavToggle from '../NavToggle'
import setMockAppContext, { defaultContext } from '../../../../testUtils/setMockAppContext'

beforeEach(() => {
  jest.resetModules()
}) 

describe('<NavToggle/>', () => { 
  //...   
  it('Should have active class if context.navOpen is true', () => {
    setMockAppContext({...defaultContext, navOpen: true})
    const NavToggle = require('../NavToggle').default //here needed to specify default export
    const component = shallow(<NavToggle/>)
    expect(component.dive().dive().hasClass('active')).toBe(true) //while shallow, I needed to dive deeper in component because of wrapping HOC   
  })
  //...
})

另一种方法是导出组件两次,一次是用 HOC 装饰的,一次是干净的组件并在其上创建测试,只是使用不同的道具测试行为。然后仅测试 HOC 作为单元,它实际上将正确的道具传递给任何包装的组件。

我想避免这种解决方案,因为我不想修改项目文件(即使它只是一个词)只是为了适应测试。


推荐阅读