NestJS — https://nestjs.com

Integration testing with NestJS and TypeORM

Paul Salmon

--

For our back-end here at Plato, we decided to give NestJS a try. NestJS is easy to set up, it helps consistency and modularity, and integrate easily with the powerful TypeORM library. As our POC started to grow, we wanted to improve the robustness of our project by adding integration tests. We already had a decent code coverage but unit tests are not quite enough as it forces us to hide the complexity of our component interaction by mocking a lot. In our case, to test the controllers, we were to mock the services and to test the services, we were to mock the repositories, which wasn’t fully satisfying.

The objectives we are trying to accomplish here are to:
- test that middleware are set up (authentication, body validation, …)
- test any side effects (database operation, API calls, …)
- test the response of the HTTP request
- test without any mock (or hardly any mock)
- test each module isolated from each other
- test automatically as part of our CD/CI

One thing we need to tell you before starting this demo: we did not want to mock the DB, we are actually spinning up a Postgres database in our CI and performing the test sequentially.

You can find the project used to write this article here.

Setting up the base project

To set up the base project, we’ll use the nest new project-name command that will create a basic project for us, and then add the library needed for our case:

nest new project-name
yarn add typeorm @nestjs/typeorm @nestjs/testing
yarn add pg jest supertest --save-dev

We will now create a user module, that will handle any operation on the user. Create a user folder in your src and first add a simple user entity:

Add the user service allowing us to list and create users using TypeORM injectable repository:

Add the controller to expose the two endpoints.

Create the user module:

We need to set up a database before running the project. To create a local database:

psql -U username -c 'create database project;'

You can now to set up TypeORM, add the default connection in your AppModule:

To access the user repository in your user service, add this line to the user module:

You should now be able to yarn start the project, create a user and list the users.

Setting up the test environment

We will need to spin up a test database to perform our end-to-end tests serially. You’ll to be able to create a test database using TypeORM:

psql -U username -c 'create database e2e_test;'TYPEORM_CONNECTION=postgres
TYPEORM_URL=postgres://username:password@localhost:5432/e2e_test
TYPEORM_ENTITIES=src/**/*.entity.ts
yarn ts-node node_modules/.bin/typeorm schema:sync

Now we have a dedicated database to perform our tests running on our localhost. In our case, this database is created and synced every time we start our test suite on our CI. To drop the local database, you can use the command: schema:drop.

Write the test

Add a user.e2e-spec.ts file to your user module and start by initiating an instance of the User module using the @nestjs/testing library. For this, you will need to import every module needed by the user service, including the repository. We can then use a TypeORM module dedicated for the test database, using our newly created database:

From this point, any repository created in your user service will use the database e2e_test. That’s how we can run our integration tests in CI, we just have to spin a Postgres database and plug it to our testing environment. The last piece you’ll need to write your tests will be a reference of the User repository:

Now you can easily perform actions on your DB before, during or after your tests. For example, something we like to do is to clean out the database after each test. You can just use the instance of the repository to clean out the table:

To write your first test, we would like to know if our GET /users endpoint would return a list of users. For this we want to populate our DB with some dummy users, then hit the endpoint to see the output:

To run the test, simply run Jest sequentially:

yarn jest --runInBand

More

TestUtils service

To simplify the tests and hide a bit the TypeORM setup complexity, we created a class to inject in our test application some handy methods for example to generate a user and a valid JWT that we pass to the authorisation header, or to clean up the whole database.

Fixtures

Have a look at RobinCK/typeorm-fixtures for a nice fixture library, to create fake data on the fly. In our case, we wrote fixtures generation function in our Test utils service to easily batch create users, companies, etc.

Mocking the logs

One thing we hate is unwanted logs in our tests, easily mock your end-to-end tests to be log free using a custom logger:

And set up the test logger in your test:

Thanks for reading!

--

--