测试异步嵌套组件

Posted

技术标签:

【中文标题】测试异步嵌套组件【英文标题】:Test asynchronous nested component 【发布时间】:2017-07-19 18:02:13 【问题描述】:

假设我有以下包装组件:

'use strict'

import React, PropTypes, PureComponent from 'react'
import update from '../../actions/actions'
import LoadFromServerButton from '../LoadFromServerButton'
import connect from 'react-redux'

export class FooDisplay extends PureComponent 
  render () 
    return (
      <p>
        <span className='foo'>
          this.props.foo
        </span>
        <LoadFromServerButton updateFunc=this.props.update />
      </p>
    )
  


export const mapStateToProps = (state) => 
  return foo: state.foo.foo


FooDisplay.propTypes = 
  foo: PropTypes.string


export const mapDispatchToProps = (dispatch) => 
  return 
    update: (foo) => dispatch(update(foo))
  


export default connect(mapStateToProps, mapDispatchToProps)(FooDisplay)

以及以下内部组件:

'use strict'

import React, PropTypes, PureComponent from 'react'
import get from '../../actions/actions'
import ActiveButton from '../ActiveButton'
import connect from 'react-redux'

export class LoadFromServerButton extends PureComponent 
  doUpdate () 
    return this.props.get().then(this.props.updateFunc)
  

  render () 
    return (
      <ActiveButton action=this.doUpdate.bind(this) actionArguments=[this.props.foo] text='fetch serverside address' />
    )
  


export const mapStateToProps = (state) => 
  return foo: state.foo.foo


export const mapDispatchToProps = (dispatch) => 
  return 
    get: () => dispatch(get())
  


LoadAddressFromServerButton.propTypes = 
  updateFunc: PropTypes.func.isRequired


export default connect(mapStateToProps, mapDispatchToProps)(LoadFromServerButton)

ActiveButton 是一个非常薄的按钮包装器,带有 onclick 和参数解构。

现在假设我的 get 操作如下所示:

export const get = () => dispatch => http('/dummy_route')
      .spread((response, body) => dispatch(actOnThing(update, body)))

现在如果我这样写一个测试:

/* global window, test, expect, beforeAll, afterAll, describe */

'use strict'

import React from 'react'
import FooDisplay from './index'
import mount from 'enzyme'
import Provider from 'react-redux'
import configureStore from '../../store/configureStore'
import nock, uriString from '../../config/nock'
import _ from 'lodash'


const env = _.cloneDeep(process.env)
describe('the component behaves correctly when integrating with store and reducers/http', () => 
  beforeAll(() => 
    nock.disableNetConnect()
    process.env.API_URL = uriString
  )

  afterAll(() => 
    process.env = _.cloneDeep(env)
    nock.enableNetConnect()
    nock.cleanAll()
  )

  test('when deep rendering, the load event populates the input correctly', () => 
    const store = configureStore(
      address: 
        address: 'foo'
      
    )
    const display = mount(<Provider store=store><FooDisplay /></Provider>,
        attachTo: document.getElementById('root'))
    expect(display.find('p').find('.address').text()).toEqual('foo')
    const button = display.find('LoadFromServerButton')
    expect(button.text()).toEqual('fetch serverside address')
    nock.get('/dummy_address').reply(200, address: 'new address')
    button.simulate('click')
  )
)

这会导致:

Unhandled rejection Error: Error: connect ECONNREFUSED 127.0.0.1:8080

经过一番思考,这是由于测试没有返回承诺,因为按钮单击导致承诺在引擎盖下触发,因此,afterAll 立即运行,清理 nock,并且一个真正的http连接通过网络。

如何测试这种情况?我似乎没有一个简单的方法来返回正确的承诺......如何测试这些更新导致的 DOM 更新?

【问题讨论】:

看起来你不是在处理一个被拒绝的承诺,而只是在它被履行时。您是否打算模拟离线环境?如果删除 nock.disableNetConnect() 及其对应项会发生什么?如果您的测试正在执行任何异步操作,您应该包含 done 参数并在异步操作完成时调用它。作为替代方案,您也可以返回测试的承诺。见facebook.github.io/jest/docs/asynchronous.html 是的,我明白,但我怎样才能返回正确的承诺?按钮单击触发异步操作,我不清楚如何强制从测试返回正确的测试 “更新”函数的定义在哪里?我觉得 this.props.getFoo() 应该是 this.props.get(),在 LoadFromServerButton 的 doUpdate 函数中? 是的,你是对的,getFoo 是用来获取的,我已经相应地更新了我的 sn-p。更新的定义与获取相同,但在帖子上而不是在获取上 【参考方案1】:

为了只模拟导入模块的一种方法,请使用 .requireActual(...)

jest.mock('../your_module', () => (
    ...(jest.requireActual('../your_module')),
    YourMethodName: () =>  return  type: 'MOCKED_ACTION'; 
));

【讨论】:

【参考方案2】:

正如您所提到的,问题是您没有承诺从测试中返回。因此,要让 get 返回一个已知的承诺,您可以直接模拟 get 而无需使用 nock:

import get from '../../actions/actions'
jest.mock('../../actions/actions', () => (get: jest.fn))

这会将动作模块替换为对象get: jestSpy

然后你可以在你的测试中创建一个promise并让get返回这个并且从你的测试中返回这个promise:

it('', ()=>
  const p = new Promise.resolve('success')
  get.mockImplementation(() => p)//let get return the resolved promise
  //test you suff
  return p
)

【讨论】:

以上是关于测试异步嵌套组件的主要内容,如果未能解决你的问题,请参考以下文章

在反应中测试嵌套组件回调

如何使用react-test-renderer和jest测试包含嵌套组件的反应组件?

如何测试 angular 2 组件,其中嵌套组件具有自己的依赖项? (TestBed.configureTestingModule)

reactjs - jest 快照测试嵌套的 redux “连接”组件

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

如何在组件中测试异步 vue 方法