我正在尝试使用JEST和酶对我的React组件进行单元测试。 在这种情况下,我想测试一个redux-form组件,所以我尝试按照本指南: https://github.com/tylercollier/redux-form-test/blob/master/tests/unit/index.js#L10
但我总是收到错误:Method “simulate” is only meant to be run on a single node. 0 found instead.
我意识到这是因为它无法找到我在.find('someElement')
中找到的元素,但我无法弄清楚如何正确访问它。我尝试将form
中的subject.find('form')
更改为MenuInputFields
和inputItemList
,但结果是一样的。
修改
我发现错误存在是因为组件以redux形式包装:
MenuInputFields = reduxForm({
form: 'inputItemList',
validate
})(MenuInputFields)
删除它可以对组件进行某种程度的测试,但这当然不是最佳的。当使用redux-forms删除连接时,我能够访问表单,但不能访问其中的任何元素,访问除表单之外的任何内容总是会产生与之前相同的错误(显示在上面)。
在我删除了redux-form-wrapping之后,我已将subject.debug()
的输出更改为结果,现在您可以看到我的字段实际上在表单中,我无法访问它们。我还更新了测试用例,以显示哪些有效,哪些无效。
我的测试:
import React from 'react'
import configureStore from 'redux-mock-store'
import Adapter from 'enzyme-adapter-react-15'
import * as sinon from 'sinon'
import * as Enzyme from 'enzyme'
import { Provider } from 'react-redux'
import { mount, shallow, render } from 'enzyme'
import { menuList } from '../../stubs/menuList'
import { MenuInputFields } from '../../components/adminMenuInputFields'
Enzyme.configure({ adapter: new Adapter() })
describe('<MenuInputFields />', () => {
let subject
let handleSubmit, pristine, reset, submitting, submitForm, touched, error
beforeEach(() => {
submitting = false
pristine = true
error = null
reset = sinon.spy()
handleSubmit = fn => fn
})
const buildSubject = () => {
submitForm = sinon.stub()
const props = {
submitForm,
initialValues: {
ClosingTime: '14:00',
MenuItems: {
FoodItem: 'test',
Description: 'test',
Price: '66'
}
},
handleSubmit,
reset
}
return shallow(<MenuInputFields {...props}/>)
}
//This works
it('it calls submitForm once when submitting', () => {
subject = buildSubject()
subject.find('form').simulate('submit')
expect(submitForm.calledOnce).toBeTruthy()
})
//This doesnt work
it('loads initial value of ClosingTime into input-box ', () => {
subject = buildSubject()
let closingTime = subject.find('form')
expect(closingTime).toEqual('14:00')
})
//This doesnt work
it('Renders the "tilføj" button', () => {
subject = buildSubject()
let button = subject.find('.addItemRow')
expect(button.text()).toEqual('Tilføj')
})
})
我的组件
class MenuInputFields extends React.Component {
constructor(props){
super(props)
}
render() {
let currentDate = new Date()
let formattedDate = currentDate.toLocaleDateString('en-GB')
const renderField = ({ input, label, type, className, meta: { touched, error } }) => (
<div>
<input {...input} type={type} placeholder={label} className={className} />
{touched && error && <span>{error}</span>}
</div>
);
const renderMenuItem = ({fields, meta: {touched, error, submitFailed }}) => (
<ul className='adminInputFoodItem'>
{fields.map((MenuItem, index) => (
<li key={index}>
<p className='col-md-1'> {index+1} - </p>
<Field
name={`${MenuItem}.FoodItem`}
type='text'
component={renderField}
label='Titel'
className='col-md-2'
/>
<Field
name={`${MenuItem}.Description`}
type='text'
component={renderField}
label='Beskrivelse'
className='col-md-6'
/>
<Field
name={`${MenuItem}.Price`}
type='number'
component={renderField}
label='Pris'
className='col-md-1'
/>
<button
type='button'
title='Fjern'
onClick={() => fields.remove(index)}
className='btn btn-default btn-xs remItemRow'
>Fjern
</button>
</li>
))}
<li>
</li>
<li>
<button
type='button'
onClick={() => fields.push({})}
className='btn btn-default btn-sm addItemRow'
>Tilføj
</button>
{submitFailed && error && <span>{error}</span>}
</li>
</ul>
);
const { handleSubmit, pristine, reset, submitting, submitForm } = this.props
return (
<form className='adminInputForm' onSubmit={handleSubmit(submitForm)}>
<label className='col-md-2' >Dato: {formattedDate}</label>
<br />
<br />
<ul className='adminInputFoodItem'>
<li>
<label className='col-md-1'> Lukketid: </label>
<Field
name='ClosingTime'
type='text'
component={renderField}
label='TT:mm'
className='col-md-1'
/>
</li>
</ul>
<hr />
<div>
<label className='col-md-1'> # </label>
<label className='col-md-2'> Titel </label>
<label className='col-md-3'> Beskrivelse </label>
<label className='col-md-1'> Pris </label>
</div>
<br />
<br />
<FieldArray name='MenuItems' component={renderMenuItem} />
<br />
<div className=''>
<button
className='btn btn-default'
type='submit'
disabled={pristine || submitting}
>Gem Menu
</button>
<label>Submit besked her</label>
</div>
</form>
)
}
}
MenuInputFields = reduxForm({
form: 'inputItemList',
validate
})(MenuInputFields)
export default MenuInputFields
呈现组件的容器:
const mapDispatchToProps = (dispatch) => {
return {
setMenu: (menu) => dispatch(setMenu(menu)),
submitForm: (newMenu) => dispatch(postMenuRequest(newMenu))
}
}
class AdminSetMenuView extends React.Component {
constructor(props){
super(props)
}
componentWillMount(){
this.props.setMenu(this.props.data)
}
render(){
return(
<div>
<AdminRoutes />
<br />
<br />
<div>
<MenuInputFields
initialValues={this.props.initialValues}
submitForm={this.props.submitForm} />
</div>
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AdminSetMenuView)
来自console.log(subject.debug())的输出:
<form className="adminInputForm" onSubmit={[Function: proxy]}>
<label className="col-md-2">
Dato:
3/8/2018
</label>
<br />
<br />
<ul className="adminInputFoodItem">
<li>
<label className="col-md-1">
Lukketid:
</label>
<Field name="ClosingTime" type="text" component={[Function: renderField]} label="TT:mm" className="col-md-1" />
</li>
</ul>
<hr />
<div>
<label className="col-md-1">
#
</label>
<label className="col-md-2">
Titel
</label>
<label className="col-md-3">
Beskrivelse
</label>
<label className="col-md-1">
Pris
</label>
</div>
<br />
<br />
<FieldArray name="MenuItems" component={[Function: renderMenuItem]} />
<br />
<div className="">
<button className="btn btn-default" type="submit" disabled={[undefined]}>
Gem Menu
</button>
<label>
Submit besked her
</label>
</div>