18

Overview:

  • I refactoring script tests before I was used Enzyme to test, but now, I want to use @testing-library/react

Problem:

  • I can't find a solution for setState in @testing-library/react
Huy Ho
  • 283
  • 1
  • 3
  • 7

1 Answers1

7

Using setState is dangerous approach regardless testing library used.

  1. It depends on implementation details(say, property names inside the state) so it becomes much harder to maintain tests - more tests to change, easy to get test broken when app is fine etc.
  2. You cannot call that once you convert class component to functional one with hooks. So you depends on implementation details even more.
  3. And finally direct state manipulation may end with state you would never get in real world. This means your component will be broken because it's impossible to reach some state but your tests with direct initialization will be fine.

So what you better do? Provide props, change props, call props(wrapper.find('button').filter(button => button.text() === 'Cancel').props().onClick() for enzyme, fireEvent.click(getByText(/Cancel/i)) for RTL) and verify against what's rendered.

This way your tests will be shorter, most actual and need less changes after you update component under test.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
  • 3
    This is a fine ideal but not very practical. What if the button isn't in this component? Should we click some other components button? That's brittle as well and breaks the idea of testing the unit. – Slobaum Dec 03 '21 at 18:40
  • How other "other component button" does affect this component's state? Passing props? Updating shared context? Navigating to browser location? Any way they communicate, you can use to indirectly set state – skyboyer Dec 03 '21 at 21:42
  • Passing a prop. Say it consumes a footer or something. That footer owns the button. We pass a state changing function to it. We can't access the prop itself, so we must click a button owned by a different component. Then sometime updates footer component to have different button text, why are tests breaking in the consuming component? This breaks unit testing – Slobaum Dec 04 '21 at 22:04
  • I don't follow, sorry. Are you talking about render prop or what? Or portals? – skyboyer Dec 04 '21 at 22:45
  • or do you mean just typical "component Footer renders component Button inside"? if this, and you really worry about "component Button can change its logic dramatically and start renredering something really different" you can _mock the Button in test for Footer_ with `jest.mock()` so you will always have `Button` to be native ` – skyboyer Dec 04 '21 at 23:07
  • Mocking the Button that this component doesn't consume is problematic. An antipattern, at best. Unit tests that bake in assumptions about implementation details of the components they consume quickly becomes brittle. You end up locked in by your unit tests. – Slobaum Dec 05 '21 at 23:13
  • 5
    This doesn't answer the question. What if I need to set a state based on waiting for an API return or a promise to resolve from some other library? It's easier to set a state with just the details I need than it would be to construct a deep mock of the entire library I'm calling. Or it would be, if there was a way to just set the state on the component. – coppereyecat May 06 '22 at 22:12