End-to-End testing React & Vue SPAs with TestCafé

Unit tests are a great asset to help ensure the quality of your application. But by nature, they only test small isolated pieces of your application and they mock important parts like API and database calls. This makes the tests very quick to execute, but it doesn’t guarantee that your application works when all the pieces are put together. End-to-End testing (E2E) plugs this gap. It is used to test that your application works when used by a real user, for example, you might have an E2E test for the entire flow of a user logging into your site, adding an item to a shopping cart and then completing the order.

Introducing TestCafé

There are many popular tools available for browser based E2E tests, but the most popular is probably Selenium WebDriver. Selenium is very powerful, but unfortunately it is not straight forward to install and get started with. TestCafe is available as a Node.js package and it doesn’t require installing any services. This makes it very easy to drop into your existing JavaScript development environment. When I first started with it, I was literally able to install it and have a basic test written in 5 minutes.

TestCafe makes it easy to test in multiple browsers, run on various CI systems, debug, take screenshots, and even run on a mobile device! I have enjoyed using it for some time now to test React and Vue.js single page applications.

Getting Setup

First install it via yarn (or npm if you prefer)

yarn add -D testcafe

Then add it to your scripts section

"scripts": {
"test:e2e:all": "testcafe chrome ./fixtures/*.test.ts"
},

In this example I am telling it to find all the tests in the fixtures folder and then run them in Chrome. You will notice that I am searching for tests written in TypeScript, change the extension to .js if you prefer JavaScript.

Writing a Test

You can write your tests in JavaScript or TypeScript. I recommend using TypeScript because you are working with async/await a lot and TypeScript will quickly catch errors such as forgetting to await something and trying to perform actions on Promise<SomeType> when you thought it was actually SomeType. TestCafe will automatically compile the TypeScript files without any extra configuration and it also includes the TypeScript declarations so you get full documentation in your IDE.

Test target. The page shows a list of “review cards”. Each with a rating and a button to read more

The above gist contains one test fixture which contains 3 basic tests to check that the home page of a Vue.js site is working correctly. The key part to any test is using the Selector API to find elements on the page. The code Selector(".home__review-container")finds all elements on a page that match the specified CSS selector and works a lot like the jQuery selector API. Hopefully the tests are self explanatory, if not, then you can read more about the basics on the TestCafe getting started page.

The golden rule: Red, Green, Refactor

The Test Driven Development Red/Green/Refactor cycle equally applies to End-to-End testing in TestCafe.

Red

Create a test and make it fail

Most of the TestCafe API is asynchronous and returns promise objects, so it is important to ensure that these promises actually complete. TestCafe is pretty good at throwing an error if you have missed an await keyword in your code. But I have had tests pass before when I failed to wait on an asynchronous assertion. So it is very important to make sure that your test initially fails to ensure that you are testing what you think you are.

Green

Make the test pass by any means necessary

Figure out all the selectors you need in your test and get it to pass.

Refactor

Change the code to remove duplication in your project and to improve the design while ensuring that all tests still pass

After writing a few tests you will see that you are using the same selectors in multiple places as well performing common actions on them e.g. clicking a button or typing some text into a text input. Since selectors commonly rely on IDs or class names, they can be quite brittle as the page develops, so it’s important to encapsulate these. TestCafe recommends using the Page Model pattern to represent items on pages.

The following example shows how the earlier example tests can be refactored into using the Page Model:

Two classes have been created (these would normally be in their own file). The Page class is used to represent the site’s home page and it includes a function to get all of the reviews on the page. There is also a ReviewCard class to represent the review card component, it has a function to get the reviews numeric rating as well as another function to perform the click action on the component. You can see that the actual tests have been simplified and are easier to read. If you can build up a good page model then you can get testers with limited programming backgrounds to help write tests.

Relying on CSS

All of your E2E tests rely on being able to reliably find elements on the page using CSS selectors. You shouldn’t run into any issues if you are writing your CSS using a convention such as BEM, but you may struggle to reliably find elements if you are using CSS Modules, Styled Components, inline styles, random legacy system spaghetti, etc. A solution might be for your React/Vue.js components to include custom data- attributes to help identify them.

Start small and build from there

I would recommend starting small with a couple of tests for your most critical happy day scenarios. This will give you a good chance to familiarise yourself with TestCafe and find out what works well for you. Keep your initial page models small and limited to what you actually require, don’t go down the rabbit hole of adding everything on the actual page into your model.

I have found that even having just a few of these scenarios coded provides a lot of benefit and removes the tedious nature of doing a cross-browser sanity check after each deployment into a test environment.

Once you’re feeling comfortable you can start checking more advanced things like comparing screenshots, checking field validation and other errors. But it’s always important to question if your new E2E tests are providing actual value and not testing things that may already be sufficiently covered in unit tests.