Unit Tests in NodeJS

jhonpedro

João Pedro

Posted on August 14, 2023

Unit Tests in NodeJS

This will be a series of Unit Tests in X language (or runtime in this case haha). But for now we will stick with the unit tests and then, maybe, we can evolve into integration tests.

I want to start this series as a way of learning tests in some languages and analyze the different aproaches that every language gives to us to start in that important topic.

The application that we will be testing is a simple ideia that I had with free and open API's. Unit tests does not involve API's but I got the idea from one of these API's.

And why Unit testing does not involve API's?

I don't know, but I think that some people would disagree with me in this point. But in my opinion, Unit tests should just test the business rule of your app. And being an API is just part of your app that you deliver to the client, your application, for example, could answer smoke signals with a sensor, or it could receive messages from a PUB/SUB third party, there are millions of options. If you disagree, PLEASE, chat with me in the comments, I would love to talk to you, and I am not even joking, I really would love to understand why you think that way.

Continuing

Free API's I said. The idea came from free food API's being more specific this one. Awesome API here, take a look, maybe you have an idea for a cool project.

Our little project will be a function (that can be atached to anything, from API's to Workers, to recomendation Algorithms), I tried to build it in a very hexagonal way because testing really depends on your architecture, aside from testing you need a good arch that allow you to test without many work arounds (in portuguese we call "gambiarra").

export class GetRandomMealUseCase {
  private userFavorites = ["potatoes", "beef"];

  constructor(private mealSerice: IMealService) {}

  async execute(): Promise<
    [Error, { recipe: IMealRecipe; choiceLevel: RandomMealChoice }]
  > {
    try {
      const meal = await this.mealSerice.getRandomMeal();

      const howManyMealTheUserLikes = meal.ingredients.filter(({ name }) =>
        this.userFavorites.includes(name.toLocaleLowerCase())
      ).length;

      const result = { recipe: meal, choiceLevel: RandomMealChoice.Average };

      if (howManyMealTheUserLikes >= 2) {
        result.choiceLevel = RandomMealChoice.ReallyGood;
      } else if (howManyMealTheUserLikes == 1) {
        result.choiceLevel = RandomMealChoice.Good;
      }

      return [null, result];
    } catch (error) {
      return [error as Error, null];
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Basically this is our whole little project. We have a service which just make a HTTP request and some basic error handling, I have choose as a error treatment something like Either where in the first position of the tuple is the error and in the second the response. I added the variable userFavorites fixed in the code, but this could be a parameter for our execute method.

We have 4 tests that I see for this little file. 1 for each choiceLevel and 1 for the error. Let's see how we can make those tests.

Mocha and SinonJS. We will be using those two fantastic libs for our tests with NodeJS. Mocha for the test cases and SinonJS for our mockings.

Unit tests

I started creating a mock service so that I could inject it in the use case.

export const getRandomMealBaseReturn: IMealRecipe = {
  name: "Mock",
  ingredients: [{ name: "Mock", measure: "1" }],
  instructions: 'Mock'
}

export class MealServiceMock implements IMealService {
  async getRandomMeal(): Promise<IMealRecipe> {
    return getRandomMealBaseReturn;
  }
}
Enter fullscreen mode Exit fullscreen mode

That is our mock of MealService. This is guy has a default return that we will mock in other tests so that it satisfies our asserts.

Our first test we just need this base implementation of the meal service to create it so look at it below

describe("GetRandomMeal service tests", () => {
  it("Should return a random meal sucessfuly with average meal choice", async () => {
    const useCase = new GetRandomMealUseCase(new MealServiceMock());

    const result = await useCase.execute();

    assert.strictEqual(result[0], null);
    assert.strictEqual(result[1].choiceLevel, RandomMealChoice.Average);
  });
}
Enter fullscreen mode Exit fullscreen mode

This first test has just the ideia of returning a meal with average choice and no errors, our next 3 tests will be just mocking the response so that we can test the other branches of the GetRandomMealUseCase and the error handling.

And that is it! Check the project over here. Like I said this will be a series of articles with this same project so, keep an eye here and study tests in other langs with me :)

Thank you!

💖 💪 🙅 🚩
jhonpedro
João Pedro

Posted on August 14, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Unit Tests in NodeJS
node Unit Tests in NodeJS

August 14, 2023