Unit-Testing for your TypeScript Node.js API

ยท

11 min read

Unit-Testing for your TypeScript Node.js API

Intro

Especially when working on larger projects, things can get quite complicated very soon. Some changes here, some minor cleanup there and BOOM - your code doesn't work (the way you want it to) anymore. As developers, we're used to testing newly implemented features, but we (usually) don't test code that was written weeks, months or even years in advance. Wouldn't it be great if we just had certain parts of our code tested automatically, in a controlled and clean environment? Entering Unit Tests.

This article will focus on the efficient testing of Node.js APIs developed in TypeScript. Therefore, prior knowledge in using TypeScript to develop Node.js APIs is assumed. If you're looking for a guide on how to migrate from JavaScript to TypeScript, head over to this article. It should get you up and running real quick.

Before we get our hands on all the technical stuff though, let's have a look at the different testing approaches for your API.

Oh, this post is accompanied by some hedgehog gifs โค I personally relate to the last one the most... my friends even labeled it live footage of me when they bring food near me ๐Ÿ˜ค

hedge1.gif

How should I test?

Well, it all depends on the amount of work you want (and can afford) to put into testing. If you're really dedicated, you might want to write tests for even the smallest chunks of your code (you know, those functions named something like checkIsEmail, for example). If you're still in your project's planning phase, Test-Drive-Development (TDD) might also be the right approach for you. However, if you're working on a larger project and implementing tests for an existing codebase, you shouldn't really (and probably don't afford to) waste too much time on testing every single crumb of logic there is to your API.

Instead, you might want to focus on testing just your endpoints, following the assumption that if your endpoints execute correctly, the logic behind does as well. Now, while that happens to be a risky assumption at times (especially when certain database interactions are considered), testing should be useful for the developer.

If it's pain, you're doing it wrong.

In a nutshell, there is no single "right" way to implement unit tests into your project. It all depends on your available resources and the role testing plays in your development process. Therefore, think carefully about the depth of your testing.

That being said, let's get our hands on some technical stuff ๐ŸŽ‰

hedge2.gif

Preparing your project

In order to implement our first tests, we need to install some packages first. We're using the testing framework Mocha in combination with the assertion library Chai. Those are very common tools and work perfectly with TypeScript. Additionally, we'll use ts-node to run Mocha. ts-node is capable of executing TypeScript code and thereby allows us to write our tests without having to compile them first. If you're interested in how that's done, read more on REPL (Read-Eval-Print Loops) here.

Run the following command in your project's root directory to install the packages:

npm install chai mocha ts-node --save-dev

Install the packages' types:

npm install @types/chai @types/mocha --save-dev

Next, we're updating our package.json file to configure Mocha and define our test command:

"scripts": {
    "test": "mocha"
},
"mocha": {
  "require": [
    "ts-node/register"
  ],
  "spec": "tests/**/*.spec.ts"
}

There are multiple configuration options available and an extensive list (although in .yml) can be found here.

It is important to require ts-node/register. Thereby, we're configuring Mocha to use ts-node for execution of our tests; as our tests are written in TypeScript, this is crucial. If you'd like to require any other files, just add their path to the array. One scenario in which you might want to add another file into the array could be the definition of environment variables for your tests. Just define them in a file and require it.

The spec option defines where our tests are located. Setting this option is advisable as it prevents Mocha from searching your entire project folder for tests. In our case, Mocha is looking for .spec.ts files inside the tests folder.

And that's it! Configuration is done, it's time to write our first test.

hedge3.gif

The first test

At the location you specified for the spec property of your Mocha configuration, create your first file sample.spec.ts.

In there, just paste the following code:

import { expect } from "chai"; // or assert, whichever you prefer
import "mocha";

const addNumbers = (a: number, b: number) => a + b;

describe("Numbers added correctly", () => {
  it("should equal 5", () => {
    const result = addNumbers(3, 2);
    expect(result).to.equal(5);
  });
});

In above test, we've defined a function addNumbers which adds two numbers (parameters a and b). Using the describe function, we're basically "grouping" our tests. Inside the callback, we're then declaring our first test. In order for our test, named should equal 5, to succeed, the result of addNumbers(3, 2) must equal 5.

In case above code appears unclear to you, check out both Mocha's and Chai's docs.

Run tests

In order to run our first test, simply run the following command which calls the test script we defined inside our package.json:

npm run test

Next, you should see a confirmation that your test succeeded. That's it. You've learned the necessary basics to write your own tests.

hedge4.gif

Test preparation & cleanup

Another useful feature of Mocha is the possibility of doing preperation/cleanup before/after execution of all tests, a group of tests or every single test. Thereby, we can ensure that our tests run in a clean environment, facing the same conditions everytime they're executed.

This is possible as Mocha collects all tests prior to executing a single one of them. Thereby, we're enabled to define hooks that run before or after tests are executed.

Let's have a look at it.

Run code before any other test is run

Doing so requires you to include below code into the root level of any test file. Alternatively, you could create a new file helper.spec.ts which takes care of global preparation/cleanup.

before((done) => {
  // do some preperation work here
  connectionManager.establishConnections(() => {
    done(); // call inside a callback to indicate that async operations are complete
  });
});

Above code is run before any tests are executed. This allows you to do preperation work that is needed for many (or all) of your tests. The done callback can be used to indicate that any asynchronous operations are done.

Run code before a group of tests is run

If you would like to do some preperation work prior to executing a specific group of tests, simply put the before function inside a describe block.

describe("Group of tests", () => {
  before((done)=> { /*...*/ }); // executes before below tests

  it("test 1", () => { /*...*/ });
  it("test 2", () => { /*...*/ });
  it("test 3", () => { /*...*/ });
});

Preperation before every single test

Using Mocha's beforeEach hook, we're enabled to run code before every test that is executed. Just like the before hook, you can either declare it globally or inside a describe block.

Cleanup

In order to do cleanup, simply use after or afterEach the same way before or beforeEach are used.

hedge5.gif

Outro

Congrats, you now know how to make use of unit tests for your TypeScript Node.js API ๐ŸŽ‚ I sincerely hope you had a nice read and learned something new. As always, if you have any questions, just comment them and I'll try my best to help you out ๐Ÿ˜Š

I would love to hear your feedback! Let me know what you liked or what could've been better with this tutorial - I always welcome constructive criticism! If you have any questions, just comment them and I'll try my best to help you out :)

In love with what you just read? Follow me for more content like this ๐Ÿ‘€

Sources

ยฉ GIFs from Giphy

๐Ÿ“„ Hooks from Mocha's official documentation

๐Ÿ“„ Chai documentation

๐Ÿ“„ ts-node package documentation

Did you find this article valuable?

Support Linus by becoming a sponsor. Any amount is appreciated!

ย