Show some love for Mocha api tests! Setting up #1
Introduction
In this story I will build simple restful api with Express
and MongoDB
. I will set up testing environment with Docker-Compose
and write api tests for all my endpoints without any mocks.
About me
I have been a Nodejs
enthusiast for almost three years already from entry level developer, to junior full stack engineer and now mid level developer. My career mostly consists of full stack development with Javascript
& Typescript
frameworks and libraries like AngularJS
, ExpressJS
, React
. I am also somewhat experienced with Devops
side of software engineering with tools like Terraform
, Docker
, Teamcity
, Nginx
and so on. My current position title is AWS cloud engineer and that’s not only my job but also a hobby as well — mastering AWS cloud.
Prerequisites
- NodeJS v12.6.0 or newer;
- NPM v6.14.0 or newer;
- Docker v19.03.13 or newer;
- Docker-compose v1.27.4 or newer;
- Visual Studio Code (or any other code editor of your preference);
- JavaScript and software development entry/junior level knowledge.
Let’s start setting up!
Initialise the project with npm init
command.
Then, install express
, mongoose
, mocha
and jsonschema
for body validation.
Scaffold project structure.
Initialise Express application with connection to Mongodb
This is very simple. Great practice is to separate actual express application from web server, hence two separate files were created: app.js
and server.js
(entry point of the application). Mongo-connect.js
module speaks for itself — it connects to Mongo
database by using mongoose library. Config.js
file will hold all the configuration variables like port, mongo connection string etc, usually these things should come from env variables, but for the purpose of simplicity I will hardcode them. And routes directory — here is the place where our actual api is going to live.
Is something missing?
Yes indeed. Mongodb
server running on docker. Here is a nice example of docker compose file for Mongodb
:
But let me duplicate this file and create another one called docker-compose-tests.yaml
:
Have you noticed the little difference? That’s right — ports. Development environment will use default 27017 port, but testing environment — 17017. That’s how we can simply ensure that we will have to separate servers for both environments. Also, here is a small update in config.js
file:
Let’s start both of these containers, the commands are almost identical (don’t forget to have docker service running on your machine):
docker-compose up -d
— this will launch the development compose file.
docker-compose -f docker-compose-tests.yaml up-d
— this will launch the testing compose file.
To bring the services down use the same commands but instead of keyword up
use keyword down.
You can validate that the containers are running with docker container ls
:
Create a model and register routes
I’m going to create a simple CRUD
api for Items
resource. The item model is going to be very simple:
Notice that I have created models/Item.js
directory and module.
Next up — registering router:
Time for restful api!
Here are five basic crud api endpoints: GET /, GET /:id, POST /, PUT /:id, DELETE /:id.
Now I’m going to do two things — set up jsonschema
validator and install express-async-wrap
to handle all async errors.
Now the easiest part — implementing all http api endpoints.
GET / — very simple api endpoint, it supports two query parameters — size and page and only allows maximum of 100 items to be retrieved at once and defaults to 10. Also, notice the X-Total-Count
header which returns how many items there are in the database.
GET /:id — even smaller api endpoint, but don’t forget to validate incoming id!
POST / — create new item in the database, but this has a key moment — always validate incoming request body. Also, notice updateItem
function — this will explicitly use only wanted fields from DTO (data transfer object). Both schema and updateItem function can and should be reused for PUT /:id endpoint.
PUT /:id has to validate both req.params.id and incoming body.
DELETE /:id — no twists and you got it right — this has to validate req.params.id:
Also, don’t forget to update error handler in app.js
after introducing NotFound
error:
That’s it.
We have our api, you can test it via postman, curl or any other tool you prefer. Next story will only consist of api integration tests with no mocks and dedicated Mongodb we have set up. You can find a second part by clicking here.