TypeScript Express tutorial #10. Testing Express applications

Express JavaScript

This entry is part 10 of 12 in the TypeScript Express tutorial

An essential part of developing a fully functional software is testing. In this article, we focus on making our application more testable and implementing unit and integration tests using Jest and a library called SuperTest. This part of the TypeScript Express testing tutorial needs some basic understanding of tests and the knowledge of tools like Jest and I encourage you to check out the first part of the JavaScript tutorial testing.

As always, to code for this tutorial is in the express-typescript repository. Since this tutorial uses Postgres, the code for it is in the postgres branch.

Testing Express with unit tests

At the beginning of the first part of the JavaScript testing tutorial, we mention unit testing. In our current application architecture, it might be challenging to separate distinct units to tests, and because of that, the first concept to get to know is a service. It can implement complex logic so that the route handlers can use them instead of doing the heavy lifting. An additional advantage to them, so significant in this article, is that we can call them anytime, anywhere, and they can be used not only when a request is made, but we can also easily use them from within our Express tests.

Let’s expand on an example of registering users that we implemented before:

src/authentication/tests/authentication.controller.ts

Thanks to moving the logic to a service, the registration method of our Authentication controller is a lot shorter than before.  Now, the service does most of the work:

src/authentication/authentication.service.ts

Since we’ve got our logic moved outside of the controller, we can start writing unit tests. The first thing to do is to install ts-jest that lets us use Jest to test projects with TypeScript:

The only thing that is left to do is to create a Jest configuration file. The ts-jest library can do it for us:

Once we got that down, we can begin testing Express! Let’s start with something simple:

src/authentication/tests/authentication.service.test.ts

You can execute it with 

ConnectionNotFoundError: Connection “default” was not found.

And we have an error! The reason of it is the fact that in the service we  access a TypeORM repository, but we don’t establish any connection. We need to fake the TypeORM functionalities so that our unit tests do not attempt to connect to a real database.

src/authentication/tests/authentication.service.test.ts

With the usage of the mockReturnValue function, we can mock our repository mock per test. It does mean that we can change it in every test. For this simple test, we don’t need anything more than just an empty object. We need to overwrite some of the TypeScript typings using any because by default the library functions are read-only.

If you want to know more about mocking functions with Jest, check out part four of the testing tutorial

Now our test passes! Let’s dig deeper and test the register function.

src/authentication/tests/authentication.service.test.ts

The first thing we check here is if the register function throws an error if the user with that email is found. This situation happens if the findOne function returns something else than undefined. We make it return the user data, and that causes the service to throw the UserWithThatEmailAlreadyExistsException.

Since we are at it, we can test the opposite situation: if the service doesn’t find the user, an error should not be thrown:

src/authentication/tests/authentication.service.test.ts

As you can see above, we need to mock more functions of the TypeORM repository. This is due to the fact that if the registration is successful, the User object is created and saved into the database.

After putting all the tests together, we can see the output:

As you can see, thanks to not connecting to any database, our tests are quite fast and don’t depend on external factors.
Thanks to that, they are more reliable.

Integration tests with SuperTest

Testing just the services might prove not to be enough at some point. With the SuperTest library, we can make requests to our application and test its behavior. Let’s install it!

One of the tasks that our controller does is attaching the Set-Cookie header with the token to the response. We can make a request to test that.

src/authentication/tests/authentication.service.test.ts

In the example above we expect the Set-Cookie header to begin with the token specified using a regular expression. The   character means that it should be the very beginning of a string and the   means that there should be one or more characters afterward.

If you want to know more about regular expressions in JavaScript, check out my RegExp course

Thanks to mocking the connection to the database, our test passes!

As you can see such tests, even without the database connection, are noticeably slower. This because we need to initialize a bigger part of our application, make it working and perform a request. Since we don’t use a real database connection, we don’t perform end-to-end Express testing here.

With the SuperTest library, you can test all types of requests as there are many possibilities. You can find more examples and the API specification in the documentation.

Summary

In this article, we covered writing unit and integration tests for our Express application with Jest and SuperTest. To avoid connecting to a real database, we mocked the functionalities of the TypeORM. If you are using something different, for example, Mongoose and MongoDB, you can surely do that as well. For the sake of making testing Express easier, we separated a part of our logic into a service. Hopefully, with implementing all of the above, your application can become more reliable making your job easier!

Series Navigation<< TypeScript Express tutorial #9. The basics of migrations using TypeORM and PostgresTypeScript Express tutorial #11. Node.js Two-Factor Authentication >>
avatar
  Subscribe  
newest oldest most voted
Notify of
Bhargab
Bhargab

How can we connect to a real database before testing?