Testing React apps E2E-style

Here’s a nice minimalistic approach I’ve recently used for testing React apps. It’s a great middle ground between the speed and configurability of unit tests and the coverage you would get only by writing end-to-end tests.

The basic idea: Render the full React app with Enzyme, simulate events on it and assert everything is rendered correctly. It’s supposed to be used to verify all the basic user flows are always working and has no opinion about the underlying technical implementation.

It works no matter which store management library you’re using.

Here’s how it works:

So for each test case, we re-render the whole app. If your app uses Redux or some other library for state management, you’ll have to make sure the store also gets recreated. Same goes for the history object if you’re using react-router or similar.

Why not test each component individually?

  • When something’s broken for the user, it doesn’t really matter if the individual components were working perfectly fine. This is to help you sleep at night knowing your core user flows are working.
  • Bugs often happen in between different components. It might happen for example that a component is passing invalid props to a component its using.
  • It’s not only the components you want to test. It’s also all of the action creators, reducers, router… Testing everything independently takes way more time both when writing and when maintaining the test cases. This method’s guaranteed to give you more 💥 for your 💵.

Why not use Cypress / Selenium / Nightwatch instead?

I’m not saying you shouldn’t. Those tools are great for verifying the complete system is functioning properly. However, I would only use such tools for testing deployments / environment integrity and integrations because:

  • Running tests on a real or a headless browser is slow. It’s unrealistic to expect everyone to run those tests every time they modify the codebase. This also makes them slow and tedious to write. Business cases tend to change more often than integrations between services.
  • End-to-end tests are also fragile. What works on someone’s shiny 15" MBP might not work on someone else’s machine. This is why I’d always run them on the same machine, for example on Travis CI.
  • Starting the whole stack, running the tests and stopping the stack with just one command can be tricky to set up. Especially considering that most developers want to have their dev environment running pretty much at all times.

Do write E2E tests, but focus on integrations between services and only run the tests on Pull Requests before they’re merged.