How to set up end-to-end tests for multiplayer apps using Puppeteer and Jest
Learn how to set up end-to-end tests with multiple browser windows using Puppeteer and Jest.
I recently tweeted a demo of one of our e2e tests, and some people were curious to know how it worked. So here we are!
For simplicity’s sake, I will show you how to test a basic realtime todo app, one of our examples.
Before we jump into the code, I want to explain why we chose Puppeteer and Jest instead of other popular e2e test solutions. If you only want to see how it works, you can skip the following sections or directly look at the code on Github.
Why not Cypress?
Usually, when I have to write e2e web tests, my go-to tool is Cypress. If you’re not familiar with Cypress, it’s a complete solution to write/record/debug/run e2e tests with a slick API/interface. It’s a great open-source tool overall.
However, Cypress has a significant limitation that I can’t ignore in the context of Liveblocks. Cypress made the permanent trade-off of not supporting multiple tabs/browsers. This trade-off is understandable and fair! Supporting multiple tabs/browsers could negatively impact API surface/developer experience for the common e2e tests.
Because we’re building APIs for collaboration, 99% of the end-to-end tests we do involve multiple browsers. Mocking is not an option because we want to go through all our infrastructure.
Why not Playwright?
I didn’t know it existed before I started writing e2e tests at Liveblocks 🙃 I never tried it, so I don’t have any opinion. Playwright supports a multi-browsers environment, so it would be a high contender for refactoring if one day Puppeteer & Jest are not enough!
Why Puppeteer & Jest?
Puppeteer is a node package that lets you control Chrome (or Firefox). It’s not made to write end-to-end tests, so you have to use it with the test framework of your choice. I decided to go with Jest because we use it for unit/integration tests.
Enough about the “Why”, show me the code!
First of all, we install a few dependencies.
The testing process is pretty simple. Every step has its own file that we
reference in the jest.config.js
.
setup.js
- Start a few browsers before launching any tests.puppeteer_environment.js
- Create a jest testing environment that will expose the browsers to a test suite. This is where we usejest-environment-node
.test/todo-list.test.js
- Navigate to the URL you want to test and validate that everything works as expected.teardown.js
- Close the browsers once all the tests are over.
In setup.js
, we’re starting multiple browsers before executing any test suite.
To keep it simple, the code below launches two instances of Chrome side to side.
It should be relatively easy to update the code to change the number of browsers
and their positions.
Puppeteer controls Chrome and Firefox via a websocket. Once the browsers are ready, we write these websocket endpoints in a temporary file that will be read from the test environment.
Then we create a custom test environment to connect to the browsers we just started. And we expose the browsers to the test suite with a global variable.
Now, the actual test. We use Puppeteers APIs to interact with the browsers/pages and then we use Jest to do the assertions.
Finally, we close all the browsers after the tests are over.
Now, you only need to run jest
to execute your test. Add the following script
in your package.json
.
And execute it with:
And voilà!
Conclusion
At this point, you probably have a good idea about how Puppeteer & Jest can work together to test multi-player apps. Puppeteer is a powerful tool; it’s also possible to launch Firefox, simulate slow network/CPU, take screenshots (for UI testing), and much more.
At Liveblocks, we’re running e2e tests before every deployment/release to ensure that all our components work well together. We also do e2e fuzz testing to find edge cases in various environments.
If you’re interested in knowing more about our engineering practices, please let us know on Github or Twitter, or even better, apply to one of our open positions.