4

I have the following code in my react component

componentDidMount() {

    ajax.call().then((data)=> {

        this.setState({a:data.a});
    });
}

Im making a simple call to a function that returns a promise.

My test looks like this when I am trying to test that the state gets set correctly from the resolved promise.

        it('should set state of a', (done)=> {

            let ajax = {
                call: ()=>{}
            };

            spyOn(ajax, 'call').and.returnValue(Promise.resolve({a:'a'}));

            let component = TestUtils.renderIntoDocument(<MyComp/>);

            expect(component.state.a).toEqual('a');
        });

I am pretty sure the state is actually getting set from the data returned by the promise, but the problem is I have nowhere to call the jasmine 'done()' function to tell jasmine the async operation has finished and that the assert on the state can be tested.

Daniel Billingham
  • 1,391
  • 5
  • 15
  • 25

1 Answers1

2

The React community mostly agrees that state is better managed through "as if' it appears synchronously so it can be snapshotted. This gives us the view as a function of a single state variable and makes our code easy to test. If you're not invested in an architecture - I suggest you try FLUX like architectures and check out common libraries like redux and este.

That said, it's perfectly fine to explicitly set state of a component after AJAX like you did. I'd separate the testing into two bits:

  • Set a state variable to the return of the AJAX, you can test this by simply checking that the object changed.
  • Call render to re-render the components you're testing (rather than setStateing).

Then, you can test the rendering and the promise setting the state differently making your rendering tests easy to reason about (they're synchronous) and the AJAX testing easy (it's just objects).

In general, when you test promises with mocha you can use the dedicated promise syntax, since you're using React you're using Babel anyway so you might as well use new JavaScript features in your code:

it('does ajax', async function(){ // can also `async () =>`
    var result = await ajax.call(); // call the ajax, get response, can be mocked
    var state = result;
    var str = React.renderToString(<MyComp prop={result} />);
    // assert against the returned HTML, can also test against DOM
});
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504