Another ExpressJS API tutorial for 2021, part 07 — Your first tests
We already configured some routes, some services and middlewares, now it is time to introduce some testings to our API

Hi there, if it is your first time arriving here we are creating a series on how to build an API using ExpressJS and Typescript. The first article can be found here.
Just as a quick overview, here is the series of articles that we are writing:
- Create your hello world ExpressJS API (Yes… we always need to start with a hello world)
- Configure our hello world application to use Typescript (TS is cool, believe me ;) )
- Create our first CRUD API endpoints for an user (Everyone wants a registered user in their applications, no?)
- Create and configure your controllers (We will talk about it after)
- Create and configure your services (What the h. is that?)
- Middleware’s! (Yes, we need and use it everyday)
- End to end testings! We are here now ;)
- Configuring a secure way to manage user permissions via API
- Putting all together into a Docker container
- Configuring a simple MongoDB to connect to our application
- Configuring permission level to the application
- Add logs with Winston!
In this articles we are going to configure our project to run tests and make sure that our implementation is working as how we are expecting. First of all, the code to continue on this article is here.
Let’s update our package.json first:
{
"name": "expressjs-api-tutorial",
"version": "0.0.1",
"description": "Tutorial of how to create a REST API using ExpressJS",
"main": "dist/app.js",
"scripts": {
"tsc": "tsc",
"start": "npm run tsc && node ./dist/app.js",
"test-dev": "nodemon --watch . --ext ts --exec \"mocha -r ts-node/register test/**/*.test.ts\"",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/makinhs/expressjs-api-tutorial.git"
},
"keywords": [
"REST",
"API",
"ExpressJS",
"NodeJS"
],
"author": "Marcos Silva",
"license": "ISC",
"bugs": {
"url": "https://github.com/makinhs/expressjs-api-tutorial/issues"
},
"homepage": "https://github.com/makinhs/expressjs-api-tutorial#readme",
"dependencies": {
"argon2-pass": "^1.0.2",
"express": "^4.17.1",
"short-uuid": "^3.1.1"
},
"devDependencies": {
"@types/chai": "^4.2.8",
"@types/express": "^4.17.2",
"@types/mocha": "^7.0.1",
"@types/mongoose": "^5.7.0",
"@types/node": "^13.5.2",
"@types/supertest": "^2.0.8",
"chai": "^4.2.0",
"mocha": "^7.0.1",
"nodemon": "^2.0.2",
"source-map-support": "^0.5.16",
"supertest": "^4.0.2",
"ts-node": "^8.6.2",
"tslint": "^6.0.0",
"typescript": "^3.7.5"
}
}Everything that we are going to use in this article are in the devDependencies which are used locally and not when the app will be running. The @types are installed to help our IDE to understand the signature of what we are using, which will speed up the way we are coding.
For testing we are going to use:
We are going to use these libraries combined to make our tests to run. Mocha will manage our application to run the tests which their library, meanwhile we are going to use chai to validate some testings, supertest to do end to end testing, calling our API, and nodemon to rerun our tests every time that we will change our source code. It seems complicated but it is super easy.
Another part that is very important is the script we added at the npm:
"test-dev": "nodemon --watch . --ext ts --exec \"mocha -r ts-node/register test/**/*.test.ts\"",We configured now a command that when we will run at our terminal npm run test-dev then our node application will use nodemon to build the application, run mocha to execute all the tests inside a folder called test (that we will create) and then run the application.
Now let’s continue configuring what we need.
At your root folder, create a file called tsconfig.json and paste the following:
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"inlineSourceMap": true,
},
"include": [
"app/**/*.ts"
],
"exclude": [
"node_modules",
"test/**/*.ts"
]
}Now we have everything that we need to start coding our tests. For that, again at the root folder let’s create a folder called test and inside it a file called app.test.ts with the following:
import {expect} from 'chai';describe("Index Test", () => {
it('should always pass', function () {
expect(true).to.equal(true);
});
});What we want here is just to make sure that we configured everything correctly. When we will run npm run test-dev we are now expecting to execute the Index Test describe function and to use expect from chai to see that everything is working. Made it? good!
Now, at the test folder, let’s create an users folder and a users.test.ts file. Here is a place that we will start to code the tests for the users. Let’s add now the following code:
import app from '../../app/app';
import {agent as request} from 'supertest';
import {expect} from 'chai';let firstUserIdTest = '';
let firstUserBody = {
"name" : "Marcos Silva",
"email" : "[email protected]",
"password" : "Pass#your!word"
};it('should POST /users', async function () {
const res = await request(app)
.post('/users').send(firstUserBody);
expect(res.status).to.equal(201);
expect(res.body).not.to.be.empty;
expect(res.body).to.be.an("object");
expect(res.body.id).to.be.an('string');
firstUserIdTest = res.body.id;
});it(`should GET /users/:userId`, async function () {
const res = await request(app)
.get(`/users/${firstUserIdTest}`).send();
expect(res.status).to.equal(200);
expect(res.body).not.to.be.empty;
expect(res.body).to.be.an("object");expect(res.body.id).to.be.an('string');
expect(res.body.name).to.be.equals(firstUserBody.name);
expect(res.body.email).to.be.equals(firstUserBody.email);
expect(res.body.id).to.be.equals(firstUserIdTest);
});it(`should GET /users`, async function () {
const res = await request(app)
.get(`/users`).send();
expect(res.status).to.equal(200);
expect(res.body).not.to.be.empty;
expect(res.body).to.be.an("array");expect(res.body[0].id).to.be.an('string');
expect(res.body[0].name).to.be.equals(firstUserBody.name);
expect(res.body[0].email).to.be.equals(firstUserBody.email);
expect(res.body[0].id).to.be.equals(firstUserIdTest);
});it('should PUT /users/:userId', async function () {
const name = 'Jose';
const res = await request(app)
.put(`/users/${firstUserIdTest}`).send({
name: name,
email: firstUserBody.email
});
expect(res.status).to.equal(204);
});it(`should GET /users/:userId to have a new name`, async function () {
const res = await request(app)
.get(`/users/${firstUserIdTest}`).send();
expect(res.status).to.equal(200);
expect(res.body).not.to.be.empty;
expect(res.body).to.be.an("object");expect(res.body.id).to.be.an('string');
expect(res.body.name).to.be.not.equals(firstUserBody.name);
expect(res.body.email).to.be.equals(firstUserBody.email);
expect(res.body.id).to.be.equals(firstUserIdTest);
});it('should PATCH /users/:userId', async function () {
let newField = {description: 'My user description'};
const res = await request(app)
.patch(`/users/${firstUserIdTest}`).send(newField);
expect(res.status).to.equal(204);
});it(`should GET /users/:userId to have a new field called description`, async function () {
const res = await request(app)
.get(`/users/${firstUserIdTest}`).send();
expect(res.status).to.equal(200);
expect(res.body).not.to.be.empty;
expect(res.body).to.be.an("object");
expect(res.body.id).to.be.an('string');
expect(res.body.description).to.be.equals('My user description');
});it('should DELETE /users/:userId', async function () {
const res = await request(app)
.delete(`/users/${firstUserIdTest}`).send();
expect(res.status).to.equal(204);
});it(`should GET /users`, async function () {
const res = await request(app)
.get(`/users`).send();
expect(res.status).to.equal(200);
expect(res.body).to.be.an("array");
expect(res.body).to.be.empty;
});What we aim here is to test our endpoints, first creating a testing user, testing it and after removing him. BUT before running, we need to add a final touch at our app.ts file adding at the end of the file the following
export default app;And that’s it. You can run now npm start test-dev and see the results. I will not be in a deep explanation on how everything works and my highly suggestion is to read each of it functions that are calling a different endpoint at our API, and try to understand how the expect function is being used. Also, try to change the API code to not work as you expect and you will see the errors that Mocha will give.
In this article we configured the tests, trying to be as much as direct as possible with a lack of hundreds of line of explanation. Each library here could be an entire article just to explain the basics. If you are curious enough please go for their websites and read more about each of it:
If you have any doubts on how to use please feel free to set some comments on this article.
Thanks for reading and see you at the next article!
tips:
- the full code of this article is here
- don’t forget to use a nice app to test your API such as Postman or Insomnia
- I know you still didn’t… Read the ExpressJS documentation!
- Complete project is here! (Includes even the non existing articles yet)
