单元测试redux表单handleSubmit

时间:2017-11-23 00:02:53

标签: reactjs unit-testing redux redux-form enzyme

我尝试在我的应用中使用redux-form,如何通过redux-form测试handleSubmit?我使用酶,之前我刚刚发现了一些组件,模拟了点击,并检查了调用args,以及触发了多少点击。当我切换到redux-form时,我不太清楚如何编写正确的单元测试并检查handleSubmit。

export const validate = device => {
    const errors = {}
    if (device.name && device.name.length > constants.defaultMaxTextFieldLength) {
        errors.name = <FormattedMessage id={tooLongErrorMessage} />
    }

    if (!device.name)
        errors.name = <FormattedMessage id={emptyErrorMessage} />

    return errors
}

export class EditDevice extends Component {
    static propTypes = {...}

    update = device => {
        device.miConfiguration.isMiEnabled = device.miConfiguration.miConfigurationType !== MiConfigurationTypes.AccessPointOnly

        this.props.update(device).then(({success, ...error}) => {
            if (!success)
                throw new SubmissionError(error)

            this.returnToList()
        })}

    returnToList = () => this.props.history.push({pathname: '/devices', state: {initialSkip: this.props.skip}})

    render = () => {
        let {isLoadingInProgress, handleSubmit, initialValues: {deviceType} = {}, change} = this.props

        const actions = [
            <Button
                name='cancel'
                onClick={this.returnToList}
            >
                <FormattedMessage id='common.cancel' />
            </Button>,
            <Button
                name='save'
                onClick={handleSubmit(this.update)}
                color='primary'
                style={{marginLeft: 20}}
            >
                <FormattedMessage id='common.save' />
            </Button>]

        return (
            <Page title={<FormattedMessage id='devices.deviceInfo' />} actions={actions} footer={actions}>
                <form onSubmit={handleSubmit(this.update)}>
                    {isLoadingInProgress && <LinearProgress mode='indeterminate'/>}
                        <Grid container>
                            <Grid item xs={12} sm={6} md={4} >
                                <Field
                                    component={renderTextField}
                                    name='name'
                                    label={<FormattedMessage id='name' />}
                                />
                            </Grid>
                          ....
                        </Grid>
                </form>
            </Page>
        )
    }
}

export const mapStateToProps = (state, {match: {params: {deviceId}}}) => {
    let initialValues = deviceId && state.entities.devices[deviceId]
    return {
        initialValues,
        deviceId,

        isLoadingInProgress: state.devices.isLoadingInProgress,
        skip: state.devices.skip,
        form: `device-${deviceId}`,
    }
}
export const mapDispatchToProps = (dispatch, {match: {params: {deviceId}}}) => ({
    init: () => dispatch(DeviceActions.get(deviceId)),
    update: device => dispatch(DeviceActions.createOrUpdate(device)),
})

export default compose(
    connect(mapStateToProps, mapDispatchToProps),
    reduxForm({validate, enableReinitialize: true, keepDirtyOnReinitialize: true}),
)(EditDevice)

以前的单元测试。

describe('EditDevice', () => {
    let init, handleSubmit, page, push

    beforeEach(() => page = shallow(<EditDevice
        handleSubmit={handleSubmit = sinon.spy()}
        deviceId={deviceId}
        history={{push: push = sinon.spy()}}
        skip={skip}
    />))
    ......

 it('should call push back to list on successful response', async () => {
        let update = sinon.stub().resolves({success: true})

        page.setProps({update})
        page.find(Field).findWhere(x => x.props().name === 'name').simulate('change', {}, 'good name')
        await page.find(Page).props().footer.find(x => x.props.name === saveButtonName).props.onClick()

        push.calledOnce.should.be.true
        push.calledWith({pathname: '/devices', state: {initialSkip: skip}}).should.be.true
    })

describe('mapStateToProps', () => {
    const deviceId = 123
    const device = {}
    const isEnabled = true
    const isLoadingInProgress = {}
    let props
    let skip = {}

    beforeEach(() => props = mapStateToProps({devices: {isLoadingInProgress, skip}, entities: {devices: {[deviceId]: device}}}, {match: {params: {deviceId}}}))

    it('should pass deviceId, form, isLoadingInProgress and skip from state', () => {
        props.deviceId.should.be.equal(deviceId)
        props.isLoadingInProgress.should.be.equal(isLoadingInProgress)
        props.skip.should.be.equal(skip)
        props.form.should.be.equal(`device-${deviceId}`)
    })
})

describe('mapDispatchToProps', () => {
    const response = {}
    const deviceId = 123
    let props

    beforeEach(() => props = mapDispatchToProps(x=> x, {match: {params: {deviceId}}}))

    it('init should call get from DeviceActions', () => {
        sinon.stub(DeviceActions, 'get').returns(response)

        props.init(deviceId).should.be.equal(response)

        DeviceActions.get.calledOnce.should.be.true
        DeviceActions.get.args[0][0].should.be.equal(deviceId)

        DeviceActions.get.restore()
    })

    it('update should call createOrUpdate from DeviceActions', () => {
        const device = {}

        sinon.stub(DeviceActions, 'createOrUpdate').returns(response)

        props.update(device).should.be.equal(response)

        DeviceActions.createOrUpdate.calledOnce.should.be.true
        DeviceActions.createOrUpdate.args[0][0].should.be.equal(device)
        DeviceActions.createOrUpdate.restore()
    })
})

0 个答案:

没有答案