Enzyme
02 / 02

State, Instance Access & Modern Alternatives

State, Instance Access & Modern Alternatives

Reading Class Component State

const wrapper = mount(<Counter />);
wrapper.find('button').simulate('click');
wrapper.update();               // re-sync wrapper with latest render after the change
expect(wrapper.state('count')).toBe(1);
expect(wrapper.instance().increment).toBeDefined();

Forgetting wrapper.update() after a state change/effect is a common gotcha — the wrapper otherwise holds a stale snapshot. .state()/.instance() are built around class-component internals and don't map cleanly onto hooks-based function components, since hook state lives in React's fiber tree, not this.state.

The Implementation-Detail Problem

Testing internal state/instance methods couples tests to HOW a component is built, not WHAT it does. Converting a class component to hooks can break such tests even though observable behavior is unchanged. This is the core motivation behind React Testing Library's philosophy: "the more your tests resemble how your software is used, the more confidence they give you."

Adapters & the Shift to RTL

enzyme-adapter-react-16/17 bridge Enzyme's core to a specific React version's internals. Keeping adapters current with newer React features (concurrent rendering) has lagged, which — combined with the internals-testing criticism above — is why React Testing Library has become the more commonly recommended default for new projects.

Snapshot Testing

import toJson from 'enzyme-to-json';

test('renders correctly', () => {
  const wrapper = shallow(<UserCard user={user} />);
  expect(toJson(wrapper)).toMatchSnapshot();
});
// caution: blindly re-approving a changed snapshot without reviewing
// the actual diff defeats the point of the check

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free