web-dev-qa-db-fra.com

Est-il possible d'empêcher ou d'espionner la méthode setState dans React lors de tests unitaires?

J'ai la fonction suivante dans mon composant:

method(args) {
 fetch(args)
  .then((response) => {
    this.setState({
      element: response
      error: false
    });
  })
  .catch((error) => {
    this.setState({
      error: true
    });
  });
}

J'essaye d'écrire un test unitaire pour cela, alors j'ai simulé l'appel d'extraction en utilisant fetch-mock avec une réponse personnalisée. Je veux vérifier si l'état a été mis à jour lorsque cette méthode est appelée et si elle ne semble pas fonctionner. J'utilise enzyme, attendez-vous et sinon, et je n'ai pas réussi à faire appeler l'espion lorsque j'ai défini composant.setState = sinon.spy ou que je l'ai écrasé. Comment puis-je vérifier si setState a été appelé avec certains éléments ou si mon approche des tests d'unité est incorrecte?

10
intuition17

Il semble que vous deviez espionner/remplacer setState dans le prototype avant d’instancier votre composant. J'avais un problème similaire où simplement espionner la méthode setState d'une instance ne fonctionnait pas. Basé sur https://medium.com/@tjhubert/react-js-testing-setstates-callback-907df1fe720d , voici une approche pour sinon:

Component.js

...
method {
  this.setState({property:'value'})
}
...

Component.test.js

...
const setState = sinon.stub(Component.prototype, ‘setState’);
const component = shallow(<Component />);
component.instance().method();
expect(setState).to.be.calledOnce;
...

Remarque: mon cas d'utilisation utilisait jasmine et évitait le rendu pour tester le comportement. La solution que je connais fonctionne alors:

Component.prototype.setState = jasmine.createSpy();
const sut = new Component();
sut.method();
expect(sut.setState).toHaveBeenCalledWith({property:'value'});
3
Will

J'ai été capable de stub setState avec les éléments suivants: 

let setStateStub = sinon.stub(prototype, 'setState')

Vous voudrez également restaurer () l'original une fois que vous avez terminé avec le stub. Vous pouvez le faire en ajoutant ce qui suit après le stub: 

setStateStub.restore()

:)

0
zero_cool

En supposant que la méthode soit appelée lorsque le composant est monté et que la méthode a été stubée, essayez ceci: 

it('should update state with response', () => {
  const wrapper = mount(<Component />);

  return Promise.resolve().then(() => {
    expect(wrapper.state('error')).to.be.false;
  });
});

Le retour d'une promesse vous permet de tester le comportement asynchrone de manière moins compliquée que d'utiliser un rappel dans setTimeout.

J'utilise sinon pour les stubs, donc j'aurais quelque chose comme ceci dans le test:

sinon.stub(window, 'fetch').resolves(mockedResponse);
0
nevace

Vous pouvez temporairement simuler/remplacer la méthode setState() du composant et attendre sa prochaine exécution. Quelque chose comme ça pourrait faire l'affaire:

function onNextSetState(component) {
  const instance = component.instance()
  const originalSetState = instance.setState.bind(instance)
  return new Promise(resolve => {
    instance.setState = (state, callback) => {
      originalSetState(state, (...args) => {
        instance.setState = originalSetState
        resolve()
        if (callback) {
          callback(...args)
        }
      }  
    }
  }
}

it('should update state with response', async () => {
  const component = mount(<Component />)
  component.instance().method()   // we assume this runs .setState inside()
  await onNextSetState(component) // we wait for the next re-render

  // we can be sure the state is what we want it to be:
  expect(component.state('error')).toBe(false)
})

Notez que si vous souhaitez attendre plus d’une fois, il sera peut-être plus facile/préférable d’utiliser quelque chose comme un observable au lieu d’une promesse.

0
niieani