Testing a JS app in 2019

Squeeds Julkalender | 2019-12-21 | Anders Teern
This post will guide you through the landscape of testing javascript applications in a way that will help you getting started in producing a reliable and well tested application. It will present my somewhat opinionated view of what your testing toolbox could (or should) look like as of 2019. It will describe my own experiences of using a lot of different tools in building my own toolbox that helps me test nearly every part of my applications.

Why should I tell you this story?

I have been writing automated tests for my javascript code since 2010, and let me tell you the best thing - the developer landscape has changed A LOT since the early days of unit testing using runners like JsTestDriver.

The ecosystem has continued to evolved presenting new test runners, test frameworks or assertion libraries all the time. For the past few years I’ve been very happy with the maturity of the platform, but it still pushes forward. And that makes me, as a developer, very happy.

How can I test feature A?

Well the answer is always: “It depends...”

Let me instead try to explain every tool or type of test I currently try to uphold.


Static
type checking

A fast tests is a good test. And a simple test is a good test. The fastest and simplest of them all is a static test. I get a lot of type checking for free since I prefer using TypeScript over Javascript. And for the more stricter static type checking I have added ESLint to the toolbox.

ESLint have a broad range of rules you can specify to enforce code quality.

To make sure every developer uses the same code formatting I have also added prettier to the toolbox, which is run automatically using husky


The
classic unit test

The smallest piece of testable code should be tested using a unit test. For this I currently use jest as my go-to tool.

These tests are simple to write and I always try to separate complex functionality inte testable pieces.


Testing
Components

In client applications nowadays we tend to produce a lot of components. I write mine as React components and try to keep my components as ”unintelligent” or “pure” as possible. As it turns out pure components require fewer tests. To be able to tests my react components i use enzyme, which will let me verify the behavior of my component with different inputs.


Interacting
with Components

Enzyme has two different major modes of testing your components. The first one - the shallow mount - let’s me run isolated tests of only the component under test, which will mock every other component in the component tree.


Integrating
2 Components

The other major mode of enzyme is the full mount which will also include other components in the render tree. This will allow you to mount two or more components and let them integrate in a controlled way.


Isolating
Components

During development there exist tools that will help you as a developer to focus on just one component at a time. Storybook is a tool that quickly will let you describe how your component could be used and what they will look like using different variants of inputs.

It will render your components in a browser and will let you both verify the visuals of your component as well as interacting with it.


Visual
regression testing Components

Since I already have all my Components defined in storybook, I might as well take full advantage of it. That is why I also use a selection of storybook add-ons. On of them is storyshots that runs visual regression tests using puppeteer against all my components. This will ensure that even minor changes to the visuals of my components will always require a verification.

A side note of using visual regression tests is that it also simplifies the Pull Request process since you always get the updated snapshots as images. Which

A modified component triggers a diff image. The original image on the left. The updated on the right. And a pixel-by-pixel diff in the middle.

A modified component triggers a diff image. The original image on the left. The updated on the right. And a pixel-by-pixel diff in the middle.

 

Integration testing

The newest tool in my toolbox is cypress, which I have learned to love for how it managed to make something that used to be frustrating and overly complex into a simple and recognizable (for javascript developers) way of writing tests. It made writing selenium/webdriver tests almost as easy as writing regular unit tests. And it also comes with a nice bundled app that makes both running and writing tests a simple task.

Cypress will let you verify that your entire application works as expected. It will let you navigate and interact with your app and verify the behavior.

 

Lessons learned

A good test always gives reproducible results. Every time! But since I’ve added the storyshot and cypress to my test suite I also had to expand the use of docker containers to make sure that the results are reproducible between different developer workstation as well as the CI pipeline.

Overlaps

So, do I always add tests on all levels for all my components?

No, I don’t. That would increase the cost of maintaining the tests, which is not desirable.

​What areas are not covered by by the current set of tools in my toolbox?

In short every touch-point towards a service or API that is outside of my control. Instead of writing tests I make sure to verify the input as early and as well as possible.

 

Summary

My tools that help me write better code as of 2019 are:

TypeScript

ESLint

Prettier

Jest

Enzyme

Storybook

Storyshots

Cypress

Docker

Comparing the list to the recently released State of JS 2019 tells me that I am definitely not the only developer praising these tools.