Reactjs 单元测试重复表单handleSubmit

Reactjs 单元测试重复表单handleSubmit,reactjs,unit-testing,redux,redux-form,enzyme,Reactjs,Unit Testing,Redux,Redux Form,Enzyme,我尝试在我的应用程序中使用redux表单,如何通过redux表单测试handleSubmit?我使用酶,之前我只是找到一些成分,模拟点击,检查调用参数,以及点击触发的次数。当我切换到redux表单时,我不太清楚如何编写正确的单元测试和检查handleSubmit export const validate = device => { const errors = {} if (device.name && device.name.length > co

我尝试在我的应用程序中使用redux表单,如何通过redux表单测试handleSubmit?我使用酶,之前我只是找到一些成分,模拟点击,检查调用参数,以及点击触发的次数。当我切换到redux表单时,我不太清楚如何编写正确的单元测试和检查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)
export const validate=device=>{
常量错误={}
if(device.name&&device.name.length>constants.defaultMaxTextFieldLength){
错误。名称=
}
如果(!device.name)
错误。名称=
返回错误
}
导出类EditDevice扩展组件{
静态propTypes={…}
更新=设备=>{
device.miConfiguration.isMiEnabled=device.miConfiguration.miConfigurationType!==MiConfigurationTypes.AccessPointOnly
this.props.update(设备)。然后({success,…error})=>{
如果(!成功)
抛出新提交错误(错误)
这个返回列表()
})}
returnToList=()=>this.props.history.push({pathname:'/devices',state:{initialSkip:this.props.skip})
渲染=()=>{
让{IsLoadingProgress,handleSubmit,initialValues:{deviceType}={},change}=this.props
常量动作=[
,
]
返回(
{IsLoadingProgress&&}
....
)
}
}
export const mapStateToProps=(状态,{match:{params:{deviceId}})=>{
让initialValues=deviceId&&state.entities.devices[deviceId]
返回{
初始值,
设备ID,
IsLoadingProgress:state.devices.IsLoadingProgress,
跳过:state.devices.skip,
格式:`device-${deviceId}`,
}
}
export const mapDispatchToProps=(调度,{match:{params:{deviceId}}})=>({
init:()=>dispatch(DeviceActions.get(deviceId)),
更新:设备=>dispatch(DeviceActions.createOrUpdate(设备)),
})
导出默认组合(
连接(mapStateToProps、mapDispatchToProps),
reduxForm({validate,enableReinitialize:true,keepDirtyOnReinitialize:true}),
)(编辑设备)
先前的单元测试

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()
    })
})
description('EditDevice',()=>{
让init、handleSubmit、page、push
之前(()=>page=shallow())
......
它('should call push back to list on successful response',async()=>{
让update=sinon.stub().resolves({success:true})
page.setProps({update})
page.find(Field).findWhere(x=>x.props().name==='name').simulate('change',{},'good name'))
等待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
})
描述('MapStateTrops',()=>{
常量设备ID=123
常量设备={}
常数isEnabled=true
常量IsLoadingProgress={}
让道具
让我们跳过={}
beforeach(()=>props=mapstatetops({devices:{isloadingprogress,skip},实体:{devices:{[deviceId]:device}}},{match:{params:{deviceId}}}))
它('应该传递deviceId、form、IsLoadingProgress和skip from state',()=>{
props.deviceId.should.be.equal(deviceId)
props.isloadingprogress.should.be.equal(isloadingprogress)
道具.跳过.应该.相等(跳过)
props.form.should.be.equal(`device-${deviceId}`)
})
})
描述('mapDispatchToProps',()=>{
常量响应={}
常量设备ID=123
让道具
beforeach(()=>props=mapDispatchToProps(x=>x,{match:{params:{deviceId}}}))
它('init应该调用从DeviceActions获取',()=>{
sinon.stub(DeviceActions,'get')。返回(响应)
props.init(deviceId).should.be.equal(响应)
DeviceActions.get.calledOnce.should.be.true
DeviceActions.get.args[0][0]。应为.be.equal(deviceId)
DeviceActions.get.restore()
})
它('更新应调用createOrUpdate from DeviceActions',()=>{
常量设备={}
sinon.stub(DeviceActions,'createOrUpdate')。返回(响应)
道具更新(设备).should.be.equal(响应)
DeviceActions.createOrUpdate.calledOnce.should.be.true
DeviceActions.createOrUpdate.args[0][0]。应.be.equal(设备)
DeviceActions.createOrUpdate.restore()
})
})