Express Integration Testing with SuperTest

Put down that REST Client (or, gasp, the browser!) you’re using to test that API you’re developing and back away slowly! There’s a better way! With SuperTest there’s no need to verify your API by hand. Plus, using it gives you virtually free integration tests. Now you can test and code new features at the same time.

I’ve recently used SuperTest to verify complex data access roles per user. If you’ve developed an API, you’ve probably had a requirement to do something similar. I might’ve picked up something like Advanced REST client. REST Clients like this can be great to check the status of an API endpoint in production, but they can quickly become tedious if you’re using as the primary means to develop your API. There was no way I’d be able to test data access accurately and often enough with a manual client. Luckily with SuperTest you gain advantages like:

Storing and using tokens to programmatically switch between users

Programatically resetting your test database

Automated testing instead of manual verification

SuperTest was a big help for my productivity and turned out to be a genuinely pleasant way to develop an API! Let’s go over how to quickly set it up for something like user registration and login. From this, you’ll be able to move into storing cookies or tokens to easily switch between sessions and test data access per user!

App Setup

SuperTest can be used with any server available on your local network (or the Internet), but it also has a super power: giving it an Express server directly. The server need not even be running! When developing, we often use tools like nodemon to automatically restart our node servers. Combine Express with SuperTest and instead of restarting your server you could automatically run your tests to verify your changes. Don’t want to run all your tests? Use Mocha’s .only specifier. There’s a lot of flexibility, we just have to set it up.

(I’ve set up a GitHub project with the code if you’d like to skip the explanations.)

Creating the project and installing dependencies

To get started create a new directory and navigate into it:

mkdir integration-tests && cd $_

Run npm init. When it asks for a test command, enter mocha '**/*.spec.js'. We’ll use this later. Feel free to answer other prompts however you’d prefer. Next, install the Express goodies:

npm i express supertest mocha chai -s

Excellent work!

Establishing working tests

We’re going to start with a barebones Express server just so we can verify our architecture works like we’re intending. To do this we’ll create our server and tests in separate files.

Create your server file touch server.js and copy and paste this into it:

The above code simply pulls in express, creates a new instance of it and gives us an endpoint to access: /api. You’ll note there’s no server being started. That’s because it’s unnecessary with SuperTest! Realistically you’ll use something like app.listen() and that will be A-OK to do, it won’t interfere with SuperTest.

Fantastic job! Reviewing the above code, we see that we’re importing our server, Chai and SuperTest. SuperTest includes its own .expect() but I prefer Chai’s syntax. The code sets a group of API Tests and creates one test to check if the endpoint /api returns a version number. Note that the done() function is important to declare these asynchronous tests complete.

Huzzah! We have tests verifying our API works. Next up? Adding some more complex testing.

Advanced Testing

So we have some integration tests being run against our newly created Express server. That’s fine, I guess. But let’s get a little more complex by adding some faux-authentication endpoints that validate parameters and return errors under different circumstances.

To get more complex, let’s add the express-validator package. It requires the Express body-parser package, so we’ll install and save both: npm i express-validator bodyparser -s. And then add them to the top of the server.js, right below the Express import:

Faux-authentication

All right, to keep it simple — and not add silly complications like passwords — we’re going to store a list of users in memory in an array. This array will reset every time our server is started. We’ll create an /api/register endpoint that pushes a user into the array and we’ll create an /api/login endpoint that returns us an item from that array. We’ll require some validation on these endpoints to ensure a valid user is created upon registration and a valid user is being requested upon login.

Let’s get started. First, copy and paste this code right below var app = new express(); in your server.js file:

This code establishes the users array we’ll be using, connects the required bodyParser (importantly done before expressValidator), and then connects expressValidator and creates a custom validator method to check if a user exists.

Now that we’ve got those out of the way, let’s add our endpoints below the code we just added:

To recap, we just added two endpoints: /api/register and /api/login. These two requests will verify that the req.body.userID is formatted how we need it. If it’s not formatted correctly, we’ll return which validation checks failed. If it is formatted correct, we’ll return the user.

Next up are the tests to see if this code actually works. Open up your tests.spec.js file and add this below our version test:

Wrapping up

That’s it! We’ve learned how to install SuperTest and connect it to Express so we test against our code quickly and efficiently. You might’ve noticed that we added validation but we never created tests for it. I’ll leave that up to you for a small code challenge. But, if you get stuck, I’ve included some tests for it in the GitHub repository. Happy testing!