Welcome to the last part of my series on how to build an API. In this tutorial I’m going to cover testing your endpoints as an application would be using them. This will provide higher level coverage for our api project and database and is a great way to test the business logic of your endpoints and database.
I did a lot of research on solutions for testing APIs as a whole. I found several solutions that can do this such as SoapUI, Postman and Karate. SoapUI and Postman provide a graphical interface, but I found that the way they do things are a bit too opinionated. In other words, you don’t have total control over what you can do in your tests. Karate seemed like a good, open-source tool but its tests are written in the Cucumber language. I preferred not introducing another language in my codebase if I could potentially make my tests work somehow with Typescript.
In this tutorial we will set up another node project that will house our jest tests. You might be wondering why we didn’t just write these tests as a part of our API project from part 2 in the series. That’s because the endpoints wouldn’t be running if we were to run npm test
. In other words, the API wouldn’t be reachable when running our tests. In addition, it’s probably good practice to separate these tests in a new project since, even though we’re only directly interfacing with our api project, we’re kind of relying on the database project as well. In addition, having these tests run as a separate project would make it easier to integrate them in a CI/CD pipeline.
Setting up the Project
We first need to create a new directory to house our project, I’m going to call it “api-tests”. Within that directory I’m going run the following command:
$ npm init
I provided my name as the author. For everything else, I used the default value by pressing enter when it asked.
Installing Dependencies
Now that we have our package.json file, we can proceed and install our dependencies.
Installing Typescript
Run the following commands in the same directory as your package.json file to install Typescript:
$ npm install typescript --save-dev
$ npm install tslint --save-dev
Installing jest
jest is a testing framework that has more than what we need to write our unit tests.
Run the following commands in the same directory as your package.json file to install jest:
$ npm install jest --save-dev
$ npm install @types/jest --save-dev
$ npm install ts-jest --save-dev
Install http-status-codes
http-status-codes makes it easy to access http status codes (200, 400, etc.)
Run the following commands in the same directory as your package.json file to install http-status-codes:
$ npm install http-status-codes --save-dev
Installing axios
axios is a http client that will make it easy for us to make requests to test our endpoint.
Run the following command in the same directory as your package.json file to install axios:
$ npm install axios --save-dev
Configuring the Project
In this section we are going to configure various elements of our project such as the typescript configuration and behavior when we attempt to start and test our project.
Create tsconfig.json
Create a file called tsconfig.json in the root of our project with the following contents:
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "es6",
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist"
},
"lib": [
"es2015"
]
}
In the configuration object above we are telling Typescript things like what version of ECMAScript we are targeting.
Configuring Our package.json File
Open our package.json file which is located in the root of our project directory. Then modify “scripts” so it looks like this:
"scripts": {
"test": "jest --runInBand --env=node"
}
This tells npm to use jest when we run npm test
. --runInBand
means that our tests won’t run in parallel. This is helpful if our tests don’t take long to complete and different tests are manipulating data that can interfere with each other’s result. --env=node
will allow us to make certain types of network requests within our tests.
Now add the following code at the end of our json file, probably after devDependencies, and don’t forget to add a comma:
"jest": {
"preset": "ts-jest"
}
The code above defines the preset we want to use with jest, our testing framework. This way jest can work with our Typescript code.
Your package.json file should now look somewhat like this:
{
"name": "api-tests",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest"
},
"author": "Andre Yonadam",
"license": "ISC",
"devDependencies": {
"@types/jest": "^25.2.1",
"axios": "^0.19.2",
"http-status-codes": "^1.4.0",
"jest": "^25.5.2",
"ts-jest": "^25.4.0",
"tslint": "^6.1.2",
"typescript": "^3.8.3"
},
"jest": {
"preset": "ts-jest"
}
}
Creating Our Tests
Tests of this nature can vary in complexity depending on what you’d like to achieve. I like to test the business logic I have in my API using these tests, so they might get a little beefy. For larger tests, I’d use the same concept of using requests on API (to add, delete, update and get data) but I’d have many in succession.
For example, let’s say our API has two routes, one for car make and one for car model and in our database the car model references the car make. With cascade behavior enabled, changing the id or deleting the entry it would also perform the same operation the dependent rows from in the car model table. We can use tests to see if this behavior works by creating a car make and a car model that references the car make, then deleting the car model and observe the dependent car make rows get deleted.
Create a Directory for Our Tests
Go ahead and create a “tests” directory in our project. We will put all of our test-related modules here.
Writing a Test
In this test, I’m going to create an entry using our hello-world route and confirm we got a response for the row that was created.
First, we must make sure the containers for our database and api are running. We can do this using a tool like Insomnia to test if our endpoints work like described in part 2 of this series. If they are not running, go ahead and start your containers by docker start container_name
for each one of your containers.
In my tests directory, I created a “hello-world.test.ts” file with the following contents:
import HttpStatus from 'http-status-codes';
import axios from 'axios';
describe("Hello World Route", () => {
test("should create an entry", async () => {
await axios
.post('http://127.0.0.1:3000/hello-world',
null,
{
params: {
message: 'This should work!'
}
}
)
.then(res => {
let id = res.data.id
expect(res.status).toEqual(HttpStatus.CREATED);
expect(res.data.id).not.toEqual(null);
})
.catch(error => {
throw new Error('Error');
})
});
});
In the code above, we declare a test with for “Hello World Route”. This individual test should pass if a entry is successfully created. As we can see, we’re using axios to make a post call and are passing a parameter, message
, to our endpoint. When we get the response back, we are then using expect() to check our response status and our data to see if it is null. If we now run npm test
our single test should pass.

Conclusion
Setting up testing for endpoints is that easy! Now, all you have to do whenever you want to run your tests is use npm test
using the project we just created.
I hope you enjoyed these three tutorials on how to build a backend. The code for this tutorial can be found at this GitHub repo.