Как протестировать componentDidUpdate()?

Это пример реализации:

export class Person extends Component {
  componentDidMount() {
    const { onLoadProfile, onLoadPolicy, person } = this.props
    onLoadProfile(person.profile.uri)
    onLoadPolicy(person.policy.uri)
  }

  componentDidUpdate(prevProps) {
    const { onLoadProfile, onLoadPolicy, person } = this.props
    const prevPerson = prevProps.person.uri
    const curPerson = person.uri

    // If person has changed, update Person component
    if (prevPerson !== curPerson) {
      onLoadProfile(person.profile.uri)
      onLoadPolicy(person.policy.uri)
    }
  }
}

На componentDidMount() мне удалось проверить это так:

describe('<Person />', () => {
  let props
  let mountedPerson
  const mockLoadProfile = jest.fn()
  const mockLoadPolicy = jest.fn()

  const person = () => {
    if (!mountedPerson) {
      mountedPerson = mount(<Person {...props} />)
    } 
    return mountedPerson
  }

  beforeEach(() => {
    props = {
      onLoadProfile = mockLoadProfile,
      onLoadPolicy = mockLoadPolicy
    }
    mountedPerson = undefined
  })

  afterEach(() => {
    mockLoadProfile.mockClear()
    mockLoadPolicy.mockClear()
  })

  describe('componentDidMount', () => {
    it('loads profile', () => {
      person().instance().componentDidMount()
      expect(mockLoadProfile).toBeCalled()
    })

    it('loads policy', () => {
      person().instance().componentDidMount()
      expect(mockLoadPolicy).toBeCalled()
    })
  })
})

В componentDidUpdate() мне нужно было бы дважды попытаться render() компонент render(), чтобы проверить, обновляется ли он, когда должен, и наоборот, но я не смог найти правильный способ сделать это.

Каков правильный подход для тестирования метода componentDidUpdate() в React?

PS: я использую шутку, энзим и React 15.

Ответ 1

Я использую другой подход, но вы можете скопировать идею. Вам нужно внести изменения в реквизиты, я использовал функцию setProps():

describe('componentDidUpdate', () => {
    it('loads profile', () => { 
        const wrapper = shallow(<Person  {...props} />) as any;
        wrapper.setProps({ person: { uri: "something_different" } });
        expect(wrapper.instance().props.onLoadProfile).toBeCalled();
    })
})

Я вижу, что розовая на странице тестирования покрытия исчезает в компонентеDidUpdate после запуска теста

Ответ 2

Принятый ответ - это самый простой способ проверить описанный выше случай, но вы также можете рассматривать его как решение, извлекающее логику componentDidUpdate из компонента, как показано ниже:

// Component definition
export class Person extends Component {
  componentDidUpdate(prevProps) {
    this.props.handleComponentDidUpdate(prevProps, this.currentProps)
  }
  // Rest of component
}

// Component functions/handlers testing
describe('Person component props', () => {
  describe('handleComponentDidUpdate', () => {
    it('loads profile & policy if person changes', () => {
      const onLoadPolicy = jest.fn()
      const onLoadProfile = jest.fn()
      const prevProps = {
        person: { uri: 'some-person-uri-old' },
        policy: { uri: 'some-policy-uri' },
        profile: { uri: 'some-profile-uri' },
        onLoadPolicy,
        onLoadProfile
      }
      const props = {
        person: { uri: 'some-person-uri-new' }, // person uri changes
        policy: { uri: 'some-policy-uri' },
        profile: { uri: 'some-profile-uri' },
        onLoadPolicy,
        onLoadProfile
      }

      handleComponentDidUpdate(prevProps, props)

      expect(onLoadPolicy).toHaveBeenCalled()
      expect(onLoadProfile).toHaveBeenCalled()
    })
  })
})

Таким образом, компонент может быть настолько глупым, насколько это возможно, а логика приложения может быть извлечена в функции/обработчики, которые легче тестируются. Таким образом, вы можете сосредоточить свое тестирование больше на функции (которую проще тестировать), а не на компоненте (что сложнее проверить).

Что касается использования компонента Person, вы просто предоставляете необходимые реквизиты, включая handleComponentDidUpdate.

Если вам все еще нужно протестировать компонент, вы можете выполнить следующий простой тест (обратите внимание, что в этом тесте, в отличие от теста функций/обработчиков, описанного выше, мы не заботимся о логике приложения, такой как персона, профиль, политика и т.д.):

// Component testing
describe('<Person/>', () => {
  it('should call handleComponentDidUpdate on prop change', () => {
    const handleComponentDidUpdate = jest.fn()
    const prevProps = {
      someProp: 'some-prop-prev',
      handleComponentDidUpdate
    }
    const newprops = {
      someProp: 'some-prop-new',
      handleComponentDidUpdate
    }

    const wrapper = shallow(<Person { ...prevProps } />)
    wrapper.setProps(newprops)

    expect(handleComponentDidUpdate).toHaveBeenCalledWith(prevProps, newProps)
  })
})