好的,所以我对如何使用酶/ jest测试组件的功能感到有点困惑。还在学习如何测试我的组件 - 我可以编写简单的测试,但现在我需要让它们更复杂。
我想知道最好的方法来测试我的组件的功能是否被正确调用,并且他们按照预期更新状态道具。我发现的很棘手的是我的功能和状态都存在于我的组件的道具中。
如果我需要使用间谍,我最好知道如何使用Jest,但如果像Sinon或Jasmine这样的人更适合这份工作,我会对它开放(只是让我知道为什么这样我可以更好地理解)。
例如,我有一个 UserDetails 组件
const UserDetails = ({
userInfo,
onUserInfoUpdate,
className,
error,
title,
primaryBtnTitle,
submit,
secondaryBtnTitle,
secondaryBtnFunc,
...props
}) => (
<div className={className}>
<div className="user-details-body">
<Section title="User details">
<TextInput
className="firstName"
caption="First Name"
value={userInfo.first}
onChange={onUserInfoUpdate('first')}
name="first-name"
min="1"
max="30"
autoComplete="first-name"
/>
<TextInput
className="lastName"
caption="Last Name"
value={userInfo.last}
onChange={onUserInfoUpdate('last')}
name="last-name"
min="1"
max="30"
autoComplete="last-name"
/>
</Section>
</div>
<div className="errorBar">
{error && <Alert type="danger">{error}</Alert>}
</div>
<ActionBar>
<ButtonGroup>
<Button type="secondary" onClick={secondaryBtnFunc}>
{secondaryBtnTitle}
</Button>
<Button type="primary" onClick={submit}>
{primaryBtnTitle}
</Button>
</ButtonGroup>
</ActionBar>
</div>
TextInput 包含:
<label className={className}>
{Boolean(caption) && <Caption>{caption}</Caption>}
<div className="innerContainer">
<input value={value} onChange={updateValue} type={type} {...rest} />
</div>
</label>
以下是我的 index.js 文件的示例代码,该文件将我的withState和withHandlers组合到我的Component:
import UserDetails from './UserDetails'
import { withState, withHandlers, compose } from 'recompose'
export default compose(
withState('error', 'updateError', ''),
withState('userInfo', 'updateUserInfo', {
first: '',
last: '',
}),
withHandlers({
onUserInfoUpdate: ({ userInfo, updateUserInfo }) => key => e => {
e.preventDefault()
updateCardInfo({
...cardInfo,
[key]: e.target.value,
})
},
submit: ({ userInfo, submitUserInfo }) => key => e => {
e.preventDefault()
submitUserInfo(userInfo)
//submitUserInfo is a graphQL mutation
})
}
})
)
到目前为止,我的测试文件如下所示:
import React from 'react'
import { mount } from 'enzyme'
import UserDetails from './'
import BareUserDetails from './UserDetails'
describe('UserDetails handlers', () => {
let tree, bareTree
beforeEach(() => {
tree = mount(
<ThemeProvider theme={theme}>
<UserDetails />
</ThemeProvider>
)
bareTree = tree.find(BareUserDetails)
})
it('finds BareUserDetails props', () => {
console.log(bareTree.props())
console.log(bareTree.props().userInfo)
console.log(bareTree.find('label.firstName').find('input').props())
})
})
控制台日志会返回正确的信息,以便我在打电话时看到我希望看到的内容:
//console.log(bareTree.props())
{ error: '',
updateError: [Function],
userInfo: { first: '', last: '' },
updateUserInfo: [Function],
onUserInfoUpdate: [Function] }
//console.log(bareTree.props().userInfo)
{ first: '', last: '' }
//console.log(bareTree.find('label.firstName').find('input).props()
{ value: '',
onChange: [Function],
type: 'text',
name: 'first-name',
min: '1',
max: '30',
autoComplete: 'first-name' }
现在问题是我如何使用它们,以及最好的方法。我是否甚至使用我的功能,或者我只是检查是否已调用onChange?
我试过这个,我得到以下内容:
it('Input element updates userInfo with name onChange in FirstName input', () => {
const firstNameInput = bareTree.find('label.firstName').find('input)
ccNameInput.simulate('change', {target: {value: 'John'}})
expect(ccNameInput.prop('onChange')).toHaveBeenCalled()
})
在我的终端,我得到:
expect(jest.fn())[.not].toHaveBeenCalled()
jest.fn() value must be a mock function or spy.
Received:
function: [Function anonymous]
但是,如果我尝试用Jest创建一个spyOn,我会收到一个错误,它无法读取'undefined'的功能。
我已经尝试了spy = jest.spyOn(UserDetails.prototypes, 'onUpdateUserInfo')
和spy = jest.spyOn(BareUserDetails.prototypes, 'onUpdateUserInfo')
,他们都抛出错误。
答案 0 :(得分:2)
我相信你应该分别测试哑组件(UserDetails)和HOC。对于哑组件,您希望使用shallow
渲染组件并注入道具。要模拟onUserInfoUpdate,您需要执行const onUserInfoUpdate = jest.fn();
你想要的东西......
import React from 'react'
import { shallow } from 'enzyme'
import UserDetails from './UserDetails'
const onUserInfoUpdate = jest.fn(); // spy
const props = {
onUserInfoUpdate,
// list all your other props and assign them mock values
};
describe('UserDetails', () => {
let tree;
beforeAll(() => {
tree = shallow(<UserDetails {...props} />)
});
it('should invoke the onUserInfoUpdate method', () => {
const firstNameInput = tree.find('label.firstName').find('input');
firstNameInput.simulate('change', { target: { value: 'John' } });
expect(onUserInfoUpdate).toHaveBeenCalledWith('first');
});
});