Ukpai Ugochi I am a female Nigerian with a Bachelor's degree in Marine engineering and Bootcamp certificates in Software development. I'm a full stack JavaScript developer (MEVN) stack. I love to share knowledge about my transition from marine engineering to software development in the form of writing, to encourage people who love software development and don't know where to begin. I also contribute to FOSS in my free time.

Using Assert modules to verify invariants in Node.js

7 min read 2004

Node.js Logo on a Brick Wall

The assert module in Node.js is used to test expressions for the functionalities in Node.js applications. If the result of the test returns false, assert throws an error and stops the program. You can use assert modules with unit test tools like unit Js, Mocha, and Chai.

Developers use assert to test for invariants in their applications. But what are invariants and what are the different methods to verify them?

What is an invariant?

Invariants are expressions or conditions that need to return true at some point in a program. Let’s look closely at the expression below:

let a = 5;
let b = 3;
let correct = (a > b);
console.log (correct);

This should return “true” when you run it on your terminal or Bash. What if, for some reason, the application above returns false and there are some other expressions depending on it?

If we change the greater than sign > to < and run this code, we should get “false.” Let’s imagine that the change in sign above is a mistake and the program returns an unexpected result because of this mistake. How would the development team trace this mistake and ensure re-occurrence does not happen?

The expression above is an example of an invariant. To guarantee that the code returns “true” as expected, Node.js development teams use the assert module to test expressions.

Types of assert methods

There are different assert methods—which one you use boils down to what you are testing invariants for in your Node.js application. To use this Node.js module, install it by running the command below:

npm i assert

Let’s dive into the different assert methods and examples of where we can use them in our application.

The Assert(value[, message]) method

This method is used for verifying if the value of an invariant is true. It’s an alias of assert.ok(value[, message]) and can be used in the same way.

//import assert module
var assert = require('assert');

//declare variables
var a = 5;
var b = 3;

//This code below would throw an assert error and end program
assert(a < b);

/**This is an expample to show how to use assert.ok(). Comment assert() out, run this code to get the same response.
assert.ok(a < b);
**/

The program above is an example of how to use the assert() method in an application. If you look closely, you’ll see that the expression is the same as the first expression used to explain invariants.

We made a custom demo for .
No really. Click here to check it out.

Note: No assert methods will throw an exception if the invariant expression returns true. They will only throw an error if invariants return false or 0.

The Assert.deepStrictEqual (actual, expected[, message]) method

This method uses assert in strict mode, so you’ll use this method when checking for equality instead of the assert.deepEqual method. This is because assert.deepEqual uses abstract equality comparison; therefore, it can produce surprising results.

An example of how to use Assert.deepStrictEqual() method is illustrated in the expression below:

//This is how to import the assert module in strict mode
const assert = require('assert').strict;

// This fails because 1 !== '1 for strict equality comparison.
assert.deepStrictEqual({ a: 1 }, { a: '1' });

/**Where { a: 1 } = actual value and { a: '1' } = expected value.
AssertionError: Expected inputs to be strictly deep-equal:
**/

The Assert.fail([message]) method

Imagine you need to add a custom message to your invariant test when your assert returns false. This is a simple type of debugging method. For instance, developers will embed the following into their codes so they know what the error is:

console.log('When you get here, log this so i know my error') 

You can insert a custom error message with the expression below:

//Import assert module
const assert = require('assert').strict;

/** AssertionError [ERR_ASSERTION]: Failed. This just prints failed as error message. This is not a good practice**/
assert.fail();

// AssertionError [ERR_ASSERTION]: This is why I failed
assert.fail('This is why I failed');

// TypeError: This is why I failed
assert.fail(new TypeError('This is why I failed'));

The Assert.ifError(value) method

Picture this: you just got an error, you probably know what line the error is coming from, but you don’t just understand why the program is throwing an error.

With the Assert.ifError method, you can test error callback arguments to understand where exactly the error is coming from. A quick example of using this method is illustrated in the expression below:

//This method is used in strict mode
const assert = require('assert').strict;

//Define error callback arguement
let err;
(function error1() {
  err = new Error('test error');
})();

//create an ifError function
(function ifError1() {
//return this error if program resolves into an error
  assert.ifError(err);
})();
/**AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error
at ifError1
at error1 **/

The Assert.doesNotMatch(string, regexp[, message]) method

This method is used to verify that the actual input string does not match the expected expression. The assert.doesNotMatch() method added in Node.js version v13.6.0, v12.16.0 is experimental and may be completely removed. This method currently uses assert in strict mode.

The simple expression below shows how to use the assert.doesNotMatch() method to verify invariants.

//import assert using strict mode
const assert = require('assert').strict;

assert.doesNotMatch('I will fail', /fail/);
/** AssertionError [ERR_ASSERTION]: The input was expected to not match because fail is contained in the actual and expected string ...**/

assert.doesNotMatch(123, /pass/);
/**AssertionError [ERR_ASSERTION]: The "string" argument must be of type string.**/

assert.doesNotMatch('I will pass', /different/);
/** This passes the test with no error since actual and expeted string have no word alike **/

The Assert.doesNotReject(asyncFn\[, error\][, message]) method

This method is used to check that promise is not rejected in an asynchronous function. If the expression does not return a promise, Assert.doesNotReject() returns a rejected promise.

Using this method is not very useful because catching a rejection just to reject it again is not beneficial. The best thing to do is to add comments next to the specific code path that should not reject and keep error messages as expressive as possible.

An example on how to use this method is shown in the expression below:

(async () => {
  await assert.doesNotReject(
    async () => {
      throw new TypeError('Wrong value');
    },
    SyntaxError
  );
})();

assert.doesNotReject(Promise.reject(new TypeError('Wrong value')))
  .then(() => {
    // ...
  });

What if you want to check if promise in an invariant was rejected?

The assert method to use in this scenario is Assert.rejects() method. This method awaits the returned promise in an asynchronous function to verify that promise is rejected.

If the promise is not rejected, it throws an error. The expression below shows simple ways we can use this method.

(async () => {
  await assert.rejects(
    async () => {
      throw new TypeError('Wrong value');
    },
    {
      name: 'TypeError',
      message: 'Wrong value'
    }
  );
})();

assert.rejects(
  Promise.reject(new Error('Wrong value')),
  Error
).then(() => {
  // ...
});

Assert in strict and legacy modes

Assert in strict mode involves using strict equality comparison method to verify invariants. In assert strict mode, non-strict methods behave as their relative strict methods.

For example, assert.deepEqual() will behave like assert.deepStrictEqual(), while assert.notEqual() will behave like assert.notStrictEqual().

To use assert in strict mode, import the assert module into your application using any of these methods:

//import the assert module using strict mode
const assert = require('assert').strict;

//import the assert module using strict mode
const assert = require('assert/strict');

In legacy mode, comparison of invariants involve the use of abstract equality comparison. It is advised to always use the strict mode to compare actual and expected values of invariants.

To import the assert module into your application using legacy mode, use the expression below:

const assert = require('assert');

Here are the different assert comparison methods in legacy mode.

The Assert.deepEqual(actual, expected[, message]) method

The assert deep equality method uses the abstract equality comparison. It is used to check for equality between actual and expected results. With this method, child properties are also tested. A quick example of how to use the assert.deepEqual() method is shown in the expression below:

const assert = require('assert');

const val1 = {
  x: {
    y: 1
  }
};
const val2 = {
  x: {
    y: 2
  }
};
const val3 = {
  x: {
    y: 1
  }
};

assert.deepEqual(val1, val3);
// Returns OK since val1 is equal to val3

// Values of b are different (Child properties)
assert.deepEqual(val1, val2);
// AssertionError: { a: { b: 1 } } deepEqual { a: { b: 2 } }

What if rather for equality, you want to test for deep inequality? The assert method to use in this case is the assert.notDeepEqual(actual, expected[, message]). This method is used for testing deep inequality and would throw an exception if the actual and expected value are equal.

An example of how to use this method is shown below:

const assert = require('assert');

const val1 = {
  x: {
    y: 1
  }
};
const val2 = {
  x: {
    y: 2
  }
};
const val3 = {
  x: {
    y: 1
  }
};

assert.notDeepEqual(val1, val3);
// AssertionError: { x: { y: 1 } } notDeepEqual { x: { y: 2 } }

/** Values of b are different (Child properties)**/
assert.notDeepEqual(val1, val2);
// Ok because val1 and val2 are not equal

Note: This method is depreciated because of its instability, so it is advised to use assert in strict mode, i.e., the Asset.deepStrictEqual and Asset.notDeepStrictEqual methods.

The Assert.equal(actual, expected[, message]) method

This method is used to test shallow comparison between the actual and expected result using abstract equality comparison. It’s hardly used to test child value as it is meant for shallow tests, and it’s also advised to use the Assert.deepStrictEqual() method instead of this method. An example of how to use Assert.equal() is shown in the expression below:

//Import the assert module
const assert = require('assert');

assert.equal(1, 1);
// This throws no exception since 1 == 1

assert.equal(1, '1');
/** This throws no exception since with abstract equality comparison 1 == '1' **/

assert.equal(NaN, NaN);
// This throws no exception

assert.equal(1, 2);
// This throws an error, AssertionError: 1 == 2

assert.equal({ a: { x: 1 } }, { a: { x: 1 } });
/**This throws an error AssertionError: { a: { x: 1 } } == { a: { x: 1 } } because it dosen't check child value**/

The assert.notEqual() method is used to check for shallow inequality, just as assert.equal() is used to check for equality.

The Assert.notEqual(actual, expected[, message]) method

Just like the assert.equal method is used for shallow equality comparison, this method is used for shallow inequality comparison.

You should not use this method for testing child value as it is meant for shallow tests.

Also, this is a legacy mode invariant method and legacy mode invariant methods are for abstract comparison. For strict comparison and also, to test for child value, use the assert.notStrictEqual method.

//Import the assert module
const assert = require('assert');

assert.notEqual(1, 1);
// This throws an exception since 1 == 1

assert.notEqual(1, '1');
/** This throws an exception since with abstract equality comparison 1 == '1' **/

assert.notEqual(1, 2);
// This throws no error, since 1 !== 2

The Assert.notDeepEqual(actual, expected[, message]) method

The assert not deep equality method is used to check for shallow inequality between actual and expected results. If you want to test for comparison among child properties, this method is a great fit for you.

A quick example of how to use the assert.notDeepEqual() method is shown in the expression below:

//Import module
const assert = require('assert');

const val1 = {
  x: {
    y: 1
  }
};
const val2 = {
  x: {
    y: 2
  }
};
const val3 = {
  x: {
    y: 1
  }
};

assert.notDeepEqual(val1, val3);
// Returns an error, since val1 is equal to val3

// Values of b are different (Child properties)
assert.deepEqual(val1, val2);
// Returns no error since the child properties are not equal

Conclusion

In this article, we’ve seen how and why we may need to use the Node.Js assert module in our applications.

It is advised to mainly use assert in strict mode while writing codes for production because their legacy counterparts may produce unexpected results, as they use abstract equality comparison, and because they are current depreciated.

You may be interested in this documentation which explains how to create a call tracker object, which will verify if invariant expressions were called the number of times required.

Thank you so much for reading this article.

200’s only Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. .
Ukpai Ugochi I am a female Nigerian with a Bachelor's degree in Marine engineering and Bootcamp certificates in Software development. I'm a full stack JavaScript developer (MEVN) stack. I love to share knowledge about my transition from marine engineering to software development in the form of writing, to encourage people who love software development and don't know where to begin. I also contribute to FOSS in my free time.

Leave a Reply