API with NestJS #131. Unit tests with PostgreSQL and Kysely

NestJS

This entry is part 131 of 180 in the API with NestJS

Creating tests is essential when trying to build a robust and dependable application. In this article, we clarify the concept of unit tests and demonstrate how to write them for our application that interacts with PostgreSQL and uses the Kysely library.

Explaining unit tests

A unit test ensures that a specific part of our code operates correctly. Using these tests, we can confirm that different sections of our system function properly on their own. Each unit test should be independent and isolated.

When we execute the command, Jest searches for files following a particular naming pattern. When we use NestJS, it looks for files that end with by default. Another common practice is to use files with the extension. You can find this configuration in our file.

package.json

Let’s start by creating a simple test for our class.

authentication.service.test.ts

PASS src/authentication/authentication.service.test.ts
The AuthenticationService
when calling the getCookieForLogOut method
✓ should return a correct string

In the example above, we’re calling the constructor manually. While this is one of the possible solutions, we can rely on NestJS test utilities to handle it for us instead. To do that, we need the method from the .

authentication.service.test.ts

Above, we create a mock of the entire NestJS runtime. Then, when we execute the method, we set up the module with its dependencies in a manner similar to how the function in our  file functions.

Avoiding connecting to the actual database

The above code has a significant issue, unfortunately. Our class connects to a real PostgreSQL database. For unit tests to be independent and reliable, we should avoid that.

Removing the from our imports causes an issue, though.

Error: Nest can’t resolve dependencies of the UsersRepository (?). Please make sure that the argument Database at index [0] is available in the UsersModule context.

To solve this problem, we need to notice that the class uses the database under the hood. We can avoid that by providing a mocked version of the .

It might make sense to refrain from mocking entire modules when writing unit tests. By doing that we would avoid testing how modules and classes interact with each other and test the parts of our system in isolation.

authentication.service.test.ts

PASS src/authentication/authentication.service.test.ts
The AuthenticationService
when calling the getCookieForLogOut method
✓ should return a correct string
when registering a new user
and when the usersService returns the new user
✓ should return the new user

Mocking the class gives us the confidence that our tests won’t use the actual database.

Changing the mock per test

In the test above, we mock the method always to return a valid user. However, that’s not always true:

  • when we type the email of an existing user in our database, the method returns the user.
  • if the user with that specific email doesn’t exist, it throws the .

Let’s create a mock that covers both cases.

authentication.service.test.ts

The AuthenticationService

when calling the getCookieForLogOut method
✓ should return a correct string
when registering a new user
and when the usersService returns the new user
✓ should return the new user

when the getAuthenticatedUser method is called
and a valid email and password are provided
✓ should return the new user
and an invalid email is provided
✓ should throw the BadRequestException

Above, we create the variable and use it in the mocked . Then, we use the method to change the value returned by the method.

An important thing to notice is that we are focusing on testing the logic contained in the methods of the while making sure that the tests don’t rely on the code of the .

Mocking Kysely

So far, we haven’t tested a class that uses Kysely directly. Let’s take a look at the method in the .

users.repository.ts

Above, we can see two cases:

  • if the database manages to create a new row in the table, the method returns an instance of the class,
  • if Kysely throws an error because the user with a particular email already exists, the method throws the .

To test both situations, we need to mock Kysely. A very important thing to notice is that we are chaining the , , and methods. Because of that, they all should return an instance of Kysely. To achieve that, we can use .

users.repository.test.ts

Please notice that we are not using for the method. Instead, we will change this mock based on the test case.

users.repository.test.ts

PASS src/users/users.repository.test.ts
The UsersRepository class
when the create method is called
and the database returns valid data
✓ should return an instance of the UserModel
✓ should return the UserModel with correct properties
and the database throws the UniqueViolation
✓ should throw the BadRequestException exception

Above, we test both cases by setting the mocked method to either return the user data or throw an error.

Summary

In this article, we’ve explained the concept of unit tests and their implementation in the context of NestJS. Our primary focus has been on testing the components of our application that interact with the database. Since unit tests should avoid using a real database connection, we’ve explored the technique of mocking our services and repositories. We also learned how to mock the Kysely library. All of the above serves as a good starting point for testing a NestJS project that uses SQL and Kysely.

Series Navigation<< API with NestJS #130. Avoiding storing sensitive information in API logsAPI with NestJS #132. Handling date and time in PostgreSQL with Kysely >>
Subscribe
Notify of
guest
3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Ahmed
Ahmed
1 year ago

can you make a blog about how to develop an abstract repository to decouple the data layer and the service layer .if the blog is in Typeorm and Postgres that will be better

Marc
Marc
5 months ago

Do you have a repo of this? I’m struggling reproducing the part where we mock Kysely, my service is still complaining for missing dep (Database) and I’d like too see the missing parts, especially how Database is defined.
Thx!