DEV Community

loading...
Cover image for Helper Functions for Assertions (Jest & Node.js)

Helper Functions for Assertions (Jest & Node.js)

remrkabledev profile image Malcolm R. Kente ใƒป3 min read

Whether you are new to software development or have been at it for a while, software testing is something you are likely to encounter. It is an integral part of any development process.

Unit Tests ๐Ÿงช

There is an expectancy for developers to write unit tests for their application's code. Unit tests are the right way of testing the smallest unit of code within a system. Ideally, these are isolated pieces of code that exist in separate files on an application.

Unit tests consist of testing individual methods and functions of your software's classes, components, or modules. Unit tests are, in general, relatively cheap to automate and can be run very quickly by a continuous integration server.

The backstory ๐Ÿ”™

So recently, I've been improving some of the projects on my GitHub. One of the main points has been to increase the code coverage by writing unit tests for my projects.

I've become quite accustomed to using the Jest testing framework. Not a knock to any other testing frameworks, but Jest has become a recent favorite go-to of sorts. Thus I decided to implement testing using Jest on a Node.js project of mine.

Disclaimer โ€ผ๏ธ Mocha/Chai could've also been a viable option, but I wanted to play around the backend with Jest this time around. ๐Ÿ™ˆ

My Problem ๐Ÿค”

Something that kept repeatedly happening is similar assertions that came up for multiple modules.

Module 1๏ธโƒฃ

isNotNumber.helper.js

module.exports = (incomingId) => isNaN(incomingId);
Enter fullscreen mode Exit fullscreen mode

isNotNumber.helper.test.js

const isNotNumber = require("../isNotNumber.helper");

describe("isNotNumber helper function unit tests", () => {
  it("should validate incoming module truthiness", () => {
    expect(isNotNumber).not.toBeNull();
    expect(isNotNumber).toBeTruthy();
  });

  it("should validate string type as true", () => {
    const results = isNotNumber("dummy")

    expect(results).not.toBe(false);
    expect(results).toBe(true);
  });

  it("should validate number type as false", () => {
    const results = isNotNumber(1)

    expect(results).not.toBe(true);
    expect(results).toBe(false);
  });
});
Enter fullscreen mode Exit fullscreen mode

Module 2๏ธโƒฃ

isObjectPropertyEmpty.helper.js

module.exports = (incomingObject) =>
  Object.values(incomingObject).some((key) => key === null || key === "");
Enter fullscreen mode Exit fullscreen mode

isObjectPropertyEmpty.helper.test.js

const isObjectPropertyEmpty = require("../isObjectPropertyEmpty.helper");

describe("isObjectPropertyEmpty helper function unit tests", () => {
  it("should validate incoming module truthiness", () => {
    expect(isObjectPropertyEmpty).not.toBeNull();
    expect(isObjectPropertyEmpty).toBeTruthy();
  });

  it("should validate object with empty field as true", () => {
    const results = isObjectPropertyEmpty({ id: "" })

    expect(results).not.toBe(false);
    expect(results).toBe(true);
  });

  it("should validate object with non-empty field as false", () => {
    const results = isObjectPropertyEmpty({ id: 1 }

    expect(results).not.toBe(true);
    expect(results).toBe(false);
  });
});
Enter fullscreen mode Exit fullscreen mode

I realized quickly that this could be an issue that may repeat over time with many of the other modules. It probably is acceptable to re-write the same assertions in multiple tests, but it felt very impractical and not-very D.R.Y.

The Solution ๐Ÿ’ก

The thing that ended up being helpful was refactoring and creating "Helper Functions" that took care of covering the repeating assertions.

The process was as simple as:

  1. Create a function(s) with the assertion logic in a separate file (use logical naming constructs). โœ…
  2. Call on the required helper when writing unit tests. โœ…

Helper functions with assertion logic
validators.js

module.exports = {
  validateTruthiness: (received) => {
    expect(received).not.toBeNull();
    expect(received).toBeTruthy();
  },
  validateBooleanValues: (received, boolean) => {
    expect(received).not.toBe(!boolean);
    expect(received).toBe(boolean);
  },
  ....
}
Enter fullscreen mode Exit fullscreen mode

Using helpers in the unit tests
isNotNumber.helper.test.js

const isNotNumber = require("../isNotNumber.helper");

const {
  validateTruthiness,
  validateBooleanValues,
} = require("../../../utils/validators");

describe("isNotNumber helper function unit tests", () => {
  it("should validate incoming module truthiness", () => {
    validateTruthiness(isNotNumber);
  });
  it("should validate string type as true", () => {
    validateBooleanValues(isNotNumber("dummy"), true);
  });

  it("should validate number type as false", () => {
    validateBooleanValues(isNotNumber(1), false);
  });
});
Enter fullscreen mode Exit fullscreen mode

isObjectPropertyEmpty.helper.test.js

const isObjectPropertyEmpty = require("../isObjectPropertyEmpty.helper");

const {
  validateTruthiness,
  validateBooleanValues,
} = require("../../../utils/validators");

describe("isObjectPropertyEmpty helper function unit tests", () => {
  it("should validate incoming module truthiness", () => {
    validateTruthiness(isObjectPropertyEmpty);
  });

  it("should validate object with empty field as true", () => {
    validateBooleanValues(isObjectPropertyEmpty({ id: "" }), true);
  });

  it("should validate object with non-empty field as false", () => {
    validateBooleanValues(isObjectPropertyEmpty({ id: 1 }), false);
  });
});
Enter fullscreen mode Exit fullscreen mode

Conclusion ๐Ÿ

That's pretty much it! Refactoring and introducing helper functions that took care of the assertion logic took away the need to repeat code.

As I soon found out, more tests also had identical assertions, so the need to have a test validation file became warranted. In the end, a single file housed all the validators used throughout the project.

I'm pretty sure other approaches to this situation I found myself in.

Let me know what you think about this?
And feel free to share any improvement tips for this. โœŒ๏ธ


Find the above code snippets ๐Ÿ‘‰ here ๐Ÿ‘ˆ
Full repo ๐Ÿ‘‰ here ๐Ÿ‘ˆ

Discussion (0)

Forem Open with the Forem app