在使用酶和玩笑对组件进行单元测试时,我们能否跳过反应挂钩(getpropivedstatefromprops)

时间:2019-07-17 18:18:12

标签: reactjs redux jestjs enzyme

在使用酶和玩笑对组件进行单元测试时,我们可以跳过反应钩子吗(“ getderivedstatefromprops”)。

我正在编写一个Counter组件,其初始状态设置为零,并且从父组件接收到新状态,同时在单元测试中模拟递增或递减函数时,由于“ getderivedstatefromprops”,更改后的状态被父项初始状态覆盖钩。在组件中,它仅被调用一次,但是在单元测试中,当我们在模拟增量函数后更新组件实例时,它会接收初始值并将状态更改为初始状态。 创建组件实例时这有什么方法吗?

export default class Counter extends React.Component<CounterProps, 
   CounterState> {
     state = {
      value: 0,
     }

   static getDerivedStateFromProps(props, state)   {
      return { ...state, value: props.initialValue }     
   }

   incrementCounter = () => {
     const { value } = this.state

     const { id, maxValue, countChangeHandler, isDisabled } = this.props

       const newValue = value + 1

       if (newValue <= maxValue) {
         countChangeHandler(id, newValue) 
       }

      this.setState({ value: newValue })
  }

  render() {   

     const { value } = this.state
     const { maxValue, isDisabled } = this.props

    return (
      <StyledCounter>
        <Count disabled={isDisabled}>{value}</Count>
        <Action onClick={this.incrementCounter}>
          <Icon shape="plusGray" size="20px" />
        </Action>
        <Label>{'max ' + maxValue}</Label>
      </StyledCounter>
    )}

}

单元测试:

describe('Counter components', () => {
  function renderCounter(customProperties = {}) {
    const properties = { 

      id: 124355,

      initialValue: 3,

      maxValue: 5,

      isDisabled: false,

      countChangeHandler: jest.fn(),
      ...customProperties,
    }
    const { output } = renderComponent(<Counter {...properties} />)

    return output
  }

  afterEach(() => {
    jest.resetAllMocks()
  })

  it('On multiple click on Increment button, counter value should not exceed max value.', () => {
    const component = renderCounter()

    component.find('div#increment').simulate('click')
    component.find('div#increment').simulate('click')
    component.find('div#increment').simulate('click')
    component.find('Counter').update()
    expect(component.find(Counter).state('value')).toEqual(5)
  })  

})

renderComponent正在使用Mount。 单元测试失败 期望5,但收到3。

1 个答案:

答案 0 :(得分:0)

因此,当组件本身不更新其某些状态并应依靠父级时,您的组件就像React中的controlled input模式一样。

我相信您不需要任何特殊技巧就可以对其进行测试。我在这里测试的内容:

  1. 更新props.value会影响render的结果
  2. 单击按钮props.countChangeHandler少于props.value + 1时用props.maxValue呼叫props.countChangeHandler
  3. 单击按钮超过props.maxValue根本不会调用const countChangeHandlerMocked = jest.fn(); function createCounter({ initialValue, maxValue }) { return shallow(<Counter initialValue={initialValue} maxValue={maxValue} id="mock_id" countChangeHandler={countChangeHandlerMocked} />); } it('renders props.value as a Count`, () => { const wrapper = createCounter({initialValue: 42, maxValue: 100}); expect(wrapper.find(Count).props().children).toEqual(42); expect(countChangeHandlerMocked).not.toHaveBeenCalled(); }); it('keeps Count in sync with props.value on update', () => { const wrapper = createCounter({initialValue: 42, maxValue: 100}); wrapper.setProps({ initialValue: 43 }); expect(wrapper.find(Count).props().children).toEqual(43); expect(countChangeHandlerMocked).not.toHaveBeenCalled(); }); it('reacts on button click if value< maxValue', () => { const wrapper = createCounter({ initialValue: 1, maxValue: 10 }); wrapper.find(Action).props().onClick(); expect(countChangeHandlerMocked).toHaveBeenCalledWith('mocked_id', 2); }); it('does not react on button click if value == maxValue', () => { const wrapper = createCount({ initialValue: 10, maxValue: 10 }); wrapper.find(Action).props().onClick(); expect(countChangeHandlerMocked).not.toHaveBeenCalled(); })
state

看,您越依赖mount()之类的实现细节或生命周期方法,测试变得越脆弱。像上面说的那样测试,很容易将组件重构为一个功能或多个功能的组合(也许我们需要使用shallow而不是gDSFP),并且组件和测试都可以。相反,过分地依赖实现细节会导致您的测试即使在合法更改后仍会失败(例如删除props.initialValue并使用state.value代替render()中的@Override public ResponseEntity<AppointmentResponse> createAppointment(AppointmentRequest partnerFulfillmentRequest) { RestTemplate rt = null; ResponseEntity<AppointmentResponse> response = null; String uri = null; HttpEntity<AppointmentRequest> httpEntity = null; HttpHeaders headers = null; try { rt = new RestTemplate(); rt.getMessageConverters().add(new MappingJackson2HttpMessageConverter()); rt.getMessageConverters().add(new StringHttpMessageConverter()); uri = new String(internalServiceUrl+"/"+APP_NAME_INTERNAL+"/appointment"); log.info("Calling internal service URL : "+uri); headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); httpEntity = new HttpEntity<AppointmentRequest>(partnerFulfillmentRequest, headers); response = rt.exchange(uri, HttpMethod.PUT, httpEntity, AppointmentResponse.class); if (response != null) { log.info("Got response from internal servicec-->statusCode: "+response.getStatusCodeValue()); log.info("Got response from internal service--> Body "+response.getBody()); } }catch(HttpClientErrorException hceEx) { //hceEx.printStackTrace(); AppointmentResponse res = new AppointmentResponse(); return new ResponseEntity<AppointmentResponse>(mapResponse(hceEx.getResponseBodyAsString()), hceEx.getResponseHeaders(), hceEx.getStatusCode()); }catch(Exception e) { e.printStackTrace(); AppointmentResponse res = new AppointmentResponse(); ResponseEntity<AppointmentResponse> wfmErrResponse = new ResponseEntity<AppointmentResponse>(res, HttpStatus.INTERNAL_SERVER_ERROR); log.error("ERROR WHILE CALLING INTERNAL SERVICE"); log.error(uri); log.error(e); return wfmErrResponse; } return response; } )。

这有意义吗?