larrydalmeida
larrydalmeida

Reputation: 1620

How to test React state after calling a component method that updates state - using Enzyme

Enzyme newbie here. I am trying to test if the state of a React component is being updated after calling a method on that component.

This is a snippet of the component that I am testing:

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  state = {
    recipes: {},
    user: null
  };

  handleUnauth = () => {
    this.setState({
      user: null
    });
  };

  render() {
   //render children, pass down methods as props etc...
  }

}

Below is the test:

import createRouterContext from 'react-router-test-context';
import { shallow, mount } from 'enzyme';
import expect from 'expect';
import React from 'react';

import App from 'App';  //import with webpack alias

describe('App', () => {

  let wrapper, context;

  beforeEach(() => {
    context = createRouterContext();
    wrapper = mount(<App/>, { context });
  });

  it('should remove logged in user details on logout', () => {
    const user = {
      uid: 'abc123',
      name: 'John Doe'
    };

    wrapper.setState({ user }, () => {
      wrapper.instance().handleUnauth();
      const userState = wrapper.state('user');
      expect(userState).toEqual(null);
    });

  });

});

My test fails with the following error:

enter image description here

I understand that updating of state is not synchronous but I am not sure if that has something to do with this or if there are better ways to test this using Enzyme. Would be super grateful if someone can please point me in the right direction. Oh and I tried this by calling wrapper.update() right after calling wrapper.instance().handleUnauth() in the test but this too didn't work.

Upvotes: 7

Views: 12499

Answers (1)

RIYAJ KHAN
RIYAJ KHAN

Reputation: 15292

From the React#setState,

setState(updater, [callback])

setState() enqueues changes to the component state. The setState doesn't immediately update the state. setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall.Instead, use componentDidUpdate or a setState callback (setState(updater, callback))

Solution 1:

Remove callback from setState;

      wrapper.setState({ user });
      wrapper.instance().handleUnauth();
      const userState = wrapper.state('user');
      expect(userState).toEqual(null);

Solution 2:

Read the updated state in the callback parameter of setState callback

wrapper.setState({ user }, (userState) => {

      wrapper.instance().handleUnauth();
      //const userState = wrapper.state('user'); // comment it.
      expect(userState).toEqual(null);
});

Upvotes: 6

Related Questions