Computer scienceBackendNode.jsCore ConceptsInternal modulesOther modules

Intro to assert module

8 minutes read

Bugs are an inevitable part of a developer's life. They are easy to find for small projects, but debugging can be really time-consuming as projects grow in size and complexity. To combat this, programmers often use testing libraries or just a simple console.log() to test their code. And the latter isn't very effective, as it doesn't provide enough information about why the program crashes. Whereas testing libraries can be too intimidating for a beginner.

As an alternative, you can turn to the assert module in Node.js. It's easy to use and offers a few methods to compare the expected and the actual output. This module is enough on its own, but you can use it in combination with a testing library as well.

Let's go over the syntax, use cases, as well as the differences between the methods.

What is assert?

The main idea of assertion is to check whether the given expression returns true or false, or if two values are equal. Basically, you give the assert module some expression to evaluate. If it returns true, you won't see anything, otherwise, it will throw an error. You can also provide two values to test against each other. For now, let's use the assert module and look at a simple example:

const assert = require("node:assert");

assert(5 > 0);

If you run this node command, you won't see anything in your terminal because the expression inside the assert method evaluates to true. Let's now change the operator to < and see if anything changes. Run the command again and you'll see an AssertionError.

AssertionError

This error occurs as 5 is not less than 0. You can also see the exact line of code which caused the error. On top of this, the assert module allows you to customize this error message by providing an optional argument to the function. For example, you can write something like this:

assert(5 < 0, "5 is less than 0");

If you test it, you should see 5 is less than 0 as a message instead of a previous one.

Assert on primitive values

When working with assertion, you should know what to check — a primitive value (number, string, boolean, etc) or a non-primitive value (object), and how you want to check it — via the strict (===) or non-strict (==) mode. This is important because the methods differ based on what and how you want to check and might be ineffective otherwise.

Here are some hints on how to choose the right method:

  • Strict mode methods have the word strict in their names;
  • If you want to compare objects, it's a good idea to look at the methods containing the word deep;
  • For primitive values in the non-strict mode, there are methods either devoid of the words strict and deep or negated ones like equal(), notEqual(), notDeepEqual(), etc.

In this section, let's work with primitives in both the strict and non-strict modes. Let's write a small, yet interesting program and try to test it. Let's suppose that a person needs around 480 hours to learn a programming language and become a Junior developer. So if a person devotes 4 hours for 5 days a week, they will gain the required knowledge in 6 months.

480 / (4 * 5 * 4 (weeks per month)) = 6

Let's write a function that can tell a person how much time (in months) they will need to learn a programming language. Users can provide days per week and hours per day that they will devote to learning the language. Based on that, the program should output a result. Focus on how assertion works. Don't worry if some math calculations aren't clear right away.

const calcLearningMonths = (daysPerWeek, dailyHours) => {
    const averageMonths = 480;
    const result = averageMonths / (daysPerWeek * dailyHours * 4);
    console.log(result);
}

calcLearningMonths(4, 5);

The program will receive the arguments for this function from the front-end. You need both of the values to be a Number, and make sure the values aren't null. Also, if someone would like to make a joke and send 0 as a value, let's catch that too. Pay special attention to how equal() and notEqual() methods are used.

const assert = require('node:assert');

const calcLearningMonths = (daysPerWeek, dailyHours) => {
   assert.equal(typeof daysPerWeek, 'number');
   assert.equal(typeof dailyHours, 'number');
   assert.notEqual(daysPerWeek, 0);
   assert.notEqual(dailyHours, 0);

   const averageMonths = 480;
   const result = averageMonths / (daysPerWeek * dailyHours * 4);
   console.log(result);
};

calcLearningMonths(0, 0);

As you can see, the function is awaiting daysPerWeek and dailyHours as numbers and doesn't want them to be equal to 0. Try to launch this program, and the program will yell because you provided 0 for the two arguments.

In this example, the non-strict mode (==) only compares the value of the parameters. It's totally fine to use, however, strict comparison can help avoid errors. As a reminder, the strict comparison (===) also checks the type of the given values. For instance, strict mode 1 isn't equal to "1" as the types are different (number vs string).

Here's how you can re-write the methods to work in strict mode:

assert.strictEqual(typeof daysPerWeek, 'number')
assert.strictEqual(typeof dailyHours, 'number');
assert.notStrictEqual(daysPerWeek, 0);
assert.notStrictEqual(dailyHours, 0);

Now that you have worked with primitives, let's move on to a more complicated data structure — Object.

Assert on non-primitive values

As noted previously, methods that work with objects usually have deep in their names. Here is a table that better represents how each method works:

Strict mode Non-strict mode
deepStrictEqual() (===) deepEqual() (==)
notDeepStrictEqual() (!==) notDeepEqual() (!=)

Let's write a small function that saves users' info to a database. Instead of the database, let's save everything locally to make things simple. Initially, you'll have an empty array of users. Then you can add users using the push method of the array.

const users = [];

const addUser = (user) => {
   users.push(user);
};

Let's add our first user and check if it was added successfully. You can check the user object itself and see if the length of an array changes or not. Initially, the length is 0. After adding the first user it should be equal to 1. Here's how you can implement this:

const assert = require('node:assert');

const users = [];

const addUser = (user) => {
   users.push(user);
};

addUser({name: "Boris Johnson", age: 58, country: "UK"});

assert.strictEqual(users.length, 1);
assert.deepStrictEqual(users, [{ name: 'Boris Johnson', age: 58, country: 'UK' }]);

Invoke the addUser function and pass in the user object as a parameter. Then check if the length of the array equals 1 and if the users array is equal to the array with the user you just passed in. Both tests pass successfully and there are no assertion errors. You can play around with this function. Try passing a few other users at once then check the output.

But what about duplication? What if Boris Johnson is already on the list? Let's change the program so that it first checks that the new user doesn't exist in the array already. You'll need a JS method called find for this case.

const assert = require('node:assert');

const users = [{ name: 'Boris Johnson', age: 58, country: 'UK' }];

const addUser = (user) => {
   const foundUser = users.find(u => u.name === user.name);
   assert.notDeepStrictEqual(user, foundUser, 'User already in the list');
   users.push(user);
};

addUser({name: "Boris Johnson", age: 58, country: "UK"});

As you can see, Boris Johnson is on the initial list. Then, in addUser() function, you first loop through the array to find a user with the same name. Finally, you compare foundUser to the new one. If they are not equal, the program will continue and push the user data into the list. Otherwise (in this case), an error will occur.

In this case an error will occur.

If you'd like to discover a few other methods, you can check out the Node.js docs.

Conclusion

The assert module in Node.js comes in handy for a lot of cases where you have to test your code. It's easy to use and allows you to work with different data types like string, number, boolean, object, and comparison types like === vs ==. If your code fails at a certain line, the module will provide a description of the error and also indicate the exact line of code that caused the problem. Moreover, the error message can be customized to fit your needs.

10 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo