API with NestJS #9. Testing services and controllers with integration tests

Express JavaScript NestJS Testing TypeScript

This entry is part 9 of 121 in the API with NestJS

In the previous part of this series, we’ve focused on unit tests. This time, we look into integration tests. In this article, we explain their principles and how they differ from unit tests. We write a few of them using Jest to test our services. We also look into the SuperTest library to test our controllers.

Testing NestJS services with integration tests

When our unit tests pass, it indicates that parts of our system work well on their own. However, an application consists of many parts that should work well together. A job of an integration test is to verify that all the cogs in the wheel integrate. We can write such tests integrating two or more parts of the system.

Let’s test how  integrates with .

src/authentication/tests/authentication.service.spec.ts

The first thing to notice above is that we mock some of the services that we use. Even though we want to write an integration test, it does not mean that we need to include every part of the system.

Mocking some parts of the system

We need to decide how many parts of the system we want to include. Let’s assume that we want to test the integration of the   and  . Going further, let’s also mock the bcrypt library.

Since our   directly imports it, it is not straightforward to mock. To do that, we need to use  .

Now that we explicitly state that we mock bcrypt, we can provide our implementation of it.

Thanks to declaring   at the top, we can now change its implementation for each test.

We do a similar thing for the repository.

Providing different implementations per test

Once we do all of the above, we can provide different implementations of our mocked services for various tests.

Above, we specify how our mocks work in the   functions. Thanks to doing that, it would run before all the tests in a particular  block.

Check out this file in the repository, if you want to inspect the above test suite thoroughly.

Testing controllers

We perform another type of integration tests by performing real requests. By doing so, we can test our controllers. It is closer to how our application is used. To do so, we use the SuperTest library.

Now, let’s test how the  integrates with   and  .

We start by mocking some of the parts of the application.

Please notice that above we also need to apply the   if we want to verify our validation.

Once we have our module ready, we can perform some tests on it. Let’s start with the registration flow.

Above, we perform real HTTP requests and test the   endpoint. If we provide valid data, we expect it to work correctly. Otherwise, we expect it to throw an error.

Aside from simple tests like those above, we can perform more throughout ones. For example, we can verify the response headers. For a full list of SuperTest features, check out the documentation.

To see the whole controller test suite, check it out in the repository.

Summary

In this article, we’ve gone through ways to write integration tests for our NestJS API. Aside from testing how our services integrate, we’ve also used the SuperTest library and tested a controller. By writing integration tests, we can thoroughly verify if our app works as expected. Therefore, it is a topic worth diving into.

Series Navigation<< API with NestJS #8. Writing unit testsAPI with NestJS #10. Uploading public files to Amazon S3 >>
Subscribe
Notify of
guest
6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Marius
Marius
3 years ago

Thanks for good tutorials.

A question about the controller test. How do you use the global ClassSerializerInterceptor in the controller test?

I see that you have reverted to setting the createdUser.password = undefined which makes this test pass.

Is there a better way where you use the Nest serializations and is the problem with it not working due to supertest?

Found a solution: https://stackoverflow.com/questions/64578498/nestjs-supertest-e2e-tests-skip-serializer-interceptors

But not sure if creating a constructor inside the User class is the best approach.

Last edited 3 years ago by Marius
tuhnik
tuhnik
3 years ago
Reply to  Marius

I agree, constructor does not look like the best approach. Instead I used plainToClass method from class-transformer.

Rafael
Rafael
2 years ago
Reply to  Marius

You can use Object.assign

Lucas Medeiros
Lucas Medeiros
3 years ago

Hi, how can we make a integration test to check if the cookie has been properly set after the login?

I’ve tryied to override localguard, but apparently the user never goes inside the request.

markflerko
2 years ago

Hi, face that error. I can’t understand why it happened:
https://i.imgur.com/qUY9s7z.png

Last edited 2 years ago by markflerko
Aliya
Aliya
1 year ago

Thank you so much for making these super useful tutorials