DEV Community

Kunwarvir
Kunwarvir

Posted on

Automated testing with jest

Introduction

Last week, I added static analysis tooling to cli-ssg. Continuing the process of managing project complexity, this week I worked on adding automated testing.

There are lots of frameworks to choose from when it comes to automated testing. However, since cli-ssg is built on Node.js, I chose to go with one of the most popular and well supported frameworks, jest to automate the testing process.

Setting up jest

Starting off by simply installing jest:

npm install --save-dev jest
Enter fullscreen mode Exit fullscreen mode

Adding a simple test script to run jest through npm in package.json:

"scripts":{
  "test": "jest"
}
Enter fullscreen mode Exit fullscreen mode

Now, I was able to run the tests using npm test. I also added a test-watch script to have jest watch for changes and run tests automatically when the test or source code is updated:

"scripts":{
  "test-watch": "jest --watch --"
}
Enter fullscreen mode Exit fullscreen mode

With jest configured, it was now time to write tests!

Writing tests:

As a starting point, I started off by testing the Options module. More specifically, I decided to test the validateInput() function which validates the this.input path and returns true if it exists:

validateInput() {
    if (!fs.existsSync(this.input)) {
      throw new Error(
        `${this.input} does not exist. Input path must be a file or directory`
      );
    }
    return true;
  }
Enter fullscreen mode Exit fullscreen mode

To test it, I created a configOptions.js file and after some back and forth this is what I came up, to test all possible scenarios for this function:

describe('validate input tests', () => {
  let options;

  beforeEach(() => {
    options = new Options();
  });

  test('empty, null and undefined paths should throw error', async () => {
    [null, undefined, ''].forEach((p) => {
      options.input = p;
      expect(() => options.validateInput()).toThrow();
    });
  });

  test('non-existing file should throw error', async () => {
    options.input = 'invalidPath';

    expect(() => options.validateInput()).toThrow(
      'invalidPath does not exist. Input path must be a file or directory'
    );
  });

  test('should validate an existing file', async () => {
    fs.existsSync.mockResolvedValue(true);

    expect(options.validateInput()).toEqual(true);
  });
});

Enter fullscreen mode Exit fullscreen mode

With the first unit test down, I wanted to test the core functionality of the SSG. To do this, I narrowed in on the convertFileToHtml(filePath, stylesheet, lang) function which converts a .txt file and generates .html output.

To test this functionality, I ended up using the beforeEach() and afterAll helpers to create and delete a sample input file using which I could then test this function:

beforeEach(() => {
    fs.writeFileSync(inputFilePath, fileInput);
  });

  afterAll(() => {
    fs.unlinkSync(inputFilePath);
  });
Enter fullscreen mode Exit fullscreen mode

The input scenarios that I tested were quite straightforward:

- html should be generated
- h1 tag should be generated from txt file
- stylesheet attribute should be rendered as a link tag
- lang attribute should be present in html
Enter fullscreen mode Exit fullscreen mode

Takeaways

With jest configured and some tests already written, we can now work on improving the code coverage. It was nice to setup automated testing since without tests, it's easy to break existing code or introduce unexpected bugs. The good news is that now, the tool can be tested by simply running npm test:
Image description

References

Github repo - https://github.com/dhillonks/cli-ssg
3d3d110 - https://github.com/dhillonks/cli-ssg/commit/3d3d1100350d885a5074a96e9757fa527fcb11c9

Top comments (0)