测试异步嵌套组件

时间:2017-02-28 16:44:37

标签: reactjs react-redux jestjs enzyme redux-thunk

说我有以下包装器组件:

'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和参数解构。

现在让我说我的行动写得如下:

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更新?

2 个答案:

答案 0 :(得分:1)

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

Format

答案 1 :(得分:0)

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

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

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

在测试中,您可以创建一个承诺,让get返回此承诺,并从您的测试中返回此承诺:

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