Is it possible to disable or peek into the setState method in React when doing unit tests?

I have the following function in my component:

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

I am trying to write a unit test for it, so I made fun of calling fetch with fetch-mock with a custom answer. I want to check if the state was updated when this method was called and it does not seem to work. I use an enzyme, expect and sine, and failed to get a spy call when I set the component .setState = sinon.spy or terminate it. How can I check if setState was called with certain elements, or my approach to the module, checking this incorrectly?

+7
unit-testing reactjs sinon enzyme
source share
4 answers

It looks like you will need to look into setState in the prototype before creating your component. I had a similar problem when simply spying on the setState method of the instance did not work. Based on https://medium.com/@tjhubert/react-js-testing-setstates-callback-907df1fe720d the approach for sinon is used here:

Componentent.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; ... 

Note: in my use case, jasmine was used and avoid rendering to test the behavior, so the solution I know looks like this:

 Component.prototype.setState = jasmine.createSpy(); const sut = new Component(); sut.method(); expect(sut.setState).toHaveBeenCalledWith({property:'value'}); 
+1
source share

Assuming the method is called when the component is mounted and the method has been muted, try the following:

 it('should update state with response', () => { const wrapper = mount(<Component />); return Promise.resolve().then(() => { expect(wrapper.state('error')).to.be.false; }); }); 

Returning the promise allows you to test asynchronous access behavior in a less negligent way than using the callback in setTimeout.

I use sinon for stubs, so I would have something like this in the test:

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

You can temporarily mock / replace the setState() method and wait for its next execution. Something like this might do the trick:

 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) }) 

Note that if you want to wait more than once, it might be easier / better to use something like Observable instead of Promise.

0
source share

I managed to stub setState with the following:

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

You will also want to restore () the original as soon as you finish with the stub. You can do this by adding the following after the stub:

 setStateStub.restore() 

:)

0
source share

All Articles