如何使用酶,开玩笑,重构,反应来测试组件中的功能

时间:2018-03-15 16:01:48

标签: reactjs enzyme jest recompose

好的,所以我对如何使用酶/ 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'),他们都抛出错误。

1 个答案:

答案 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');
  });
});