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 checkKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free