Lisa Tassone

My front-end testing stack 2023

Testing front-end code extensively usually requires multiple testing tools and frameworks.

What came before...

A few years ago, the main tools I used were:

  • ESLint (catch common coding mistakes)
  • TypeScript (typed JavaScript)
  • Jest (unit testing)
  • Testing Library for React (for React hooks and component integration tests)
  • Cypress and then Playwright (end-to-end tests for critical and multi-page customer user journeys that are hard to test in any other way.)
  • GraphQL (typed server APIs). This was an architecture choice for the company I worked for at the time. Since it did provide type safety to our front-end, I've included it as part of the stack. Testing is all about having confidence in your code, and if you use technologies that enable more of that, its less you need to test and that's a good thing.

When testing container or page type components that were responsible for fetching data and orchestrating the state, the fetching hooks would be mocked and return the response or loading states the front-end expected. While this let me test a lot of the component tree interactions and states, that approach essentially bypasses the lifecycle methods of these hooks so it wasn't a true integration test when it came to that sort of logic.

While Storybook was employed for developing UI components in isolation, I never saw it as a testing tool because at that stage there wasn't much in the way of testing add-ons for it.

Not the case anymore!

Over the last couple of years, the community, Storybook and Chromatic have made it into a complete development offering.

What's changed?

Storybook introduced interaction testing early last year which allows Stories to run tests that emulate user behaviour just like an end-to-end test. The way it works is you write your test with an augmented version of Testing Library, and Storybook generates a JSON payload of instructions that is converted into a Playwright test. How cool is that!

The benefits of this approach are:

  • you can see your test run in real time
  • you are testing against a real browser
  • you (or library creators) don't have to mock or work around browser API's that js-dom does not support (contenteditable I'm looking at you)

Chromatic is run by Storybook core maintainers and offers version controlled visual regression testing, storybook hosting, story reviewing and more.

Between these 2 offerings you have a large part of your testing efforts covered. I still use Jest for unit testing, but the only time I've found myself reaching for Testing Library now outside of Storybook, has been for testing hook specific logic that I felt was overly heavy to test in an interaction test. Browser tests are heavy so it still makes sense to use a lower level tool that's capable of doing the same thing faster, when you can.

How to improve the "page" type components with data fetching?

Mock Service Worker makes short work of this. It uses a service worker to intercept network requests from you code and allows you to mock a response at that level. Storybook has an add-on for this as well, so you can develop and test entire pages in storybook and ensure that all the lifecycle methods of fetching are triggered and real.

You can effectively code offline if you really wanted to!

REST APIs - aka, not GraphQL

If you aren't using TypeScript to write REST APIs but are using it on the front-end, a combination of validated OpenAPI specs and TypeScript type generation based on those specs, can work really nicely here. You need to ensure that you aren't just validating whether the OpenAPI spec is adhering to the standard, but also that what you are saying it returns is true. Many languages have packages that can do this.

I've only had limited experience so far with using PACT contract testing to validate client server APIs. While it works, there's a fair bit to get your head around and the collaboration cost between a front-end team and a back-end team was a bit of a surprise to me. Depending on the model you implement, a FE can declare what they are expecting (consumer-driven), but the backend needs to write tests against that declaration which means they need to know how to interpret what you are doing from limited supplied states. For distributed teams that don't work close to each other, I would imagine this would be a major drawback.

My preferred testing stack as at 2023

Unit testing

Visual unit tests

Chromatic

Logic unit tests

Jest test runner in combination with React testing library for hooks

ESlint for catching common mistakes or 'smells'.

Integration testing

Storybook interaction tests utilising Mock Service Worker.

End-to-end testing

Playwright (and you know, the entire infrastructure around it to support the testing environment)

Server -> Client contracts

I prefer to use TypeScript and Node.js for APIs and share/publish the types for consumption on the front-end.