Adding tests to my project
Tags: webdev, javascript, programming, beginners
Published: 2023-11-07 02:18:08

## Commit
[3dfea570](https://github.com/sdthaker/Node-TILify/commit/3dfea570302a5eb2ac7824c9bb4478fff048591d)

## Lab

Tasks completed for this week's lab: 
* Set up a testing environment for my codebase using [Jest](https://jestjs.io/). 
* Wrote a few tests to introduce unit testing for my codebase which tested the core of the codebase. 
* Added a test runner to watch for code changes for the code or test cases to re-rerun the tests and also allow the ability to simply run a single test, a single test suite or a single test file. 
* Updated documentation file, CONTRIBUTING.md to add usage of testing.
* Added code coverage to check for how many lines of code is tested using the testing framework.

## Setting up Jest

* Set up Jest by installing it as a dev dependency using the command `npm install --save-dev jest`. Then I added `tests` directory to write my tests. 
* Installed 3 dependencies, `babel/core`, `babel/preset-env` & `babel-jest` to make sure it supports the `import` syntax in test files and added `babel.config.cjs` config file with options.
* Added `jest: true` to the `env` object located in `.eslintrc.cjs` file for ESLint to recognize jest's global variables like `describe`, `it`, etc.
* Added `test` script which in turn runs `jest --`.

## Writing first Jest tests
I picked a very small function to test its input and output. The function reads & parses TOML files using a third-party library. In one test suite, I wrote four tests, one of them testing the try path and the other three testing the catch part. It was somewhat of a hiccup to get these tests to set up because the code in the catch block performed `console.error()` and `process.exit()` operations. So I had to read the API to find out how to test these operations. That's when I stumbled upon mock functions `spyOn()` & `mockImplementation()`. [Mock functions are also known as "spies", because they let you spy on the behaviour of a function that is called indirectly by some other code, rather than only testing the output](https://jestjs.io/docs/mock-function-api). So I spied on the 2 operations and was able to match the result provided by the function, with the result that I was expecting written in the test cases. A code example is below,

```javascript
it('function should exit with error message & exit code -1 with incorrect toml file path', () => {
    const mockStdErr = jest
      .spyOn(global.console, 'error')
      .mockImplementation(() => {});
    const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
    readAndParseTomlConfig('file/path/that/doesnt/exist.toml');

    expect(mockStdErr).toHaveBeenCalledWith(
      'Error reading or parsing TOML file'
    );
    expect(mockExit).toHaveBeenCalledWith(-1);
  });
```

## Allowing to run single tests and Test Runner Improvements
I added the capability to run a single file, test case or test suite using a single script `npm run test:single blob`. Here the blob argument can be a filename, test case or test suite. Also, I added the ability to have my test runner watch for changes and automatically run the tests when the test or source code is updated by using a single script `npm run test:watch`.

## Test the Core
I added three tests part of the suite to test the core functionality of the project which is parsing MD to HTML. The three tests focus on testing the file I/O operations, specifically reading from a file, writing to a file and checking whether a file or directory exists. I added mock tests for it to simulate a real operation to test the functionality.

## Adding code coverage analysis
Added code coverage analysis tool to test which files are tested and covered in testing and how much of the code is being missed out / tested on by running a single script `npm run test:coverage`.

## Ignoring unnecessary folders
After running code coverage, it generates a folder named `coverage`. This folder is generated as a result of running a code coverage script. So there's no need to format, lint or commit the folder to GitHub. So I added the folder as part of `.gitignore`, `.prettierignore` & `.eslintignore` files.

## Updating CONTRIBUTING.md
All of the above changes regarding tests have also been documented for ease of use.

## Learning Outcomes
I understood the fundamental understanding of modern software testing practices by adding a test framework to my project. I learnt how to write concise and efficient test suites, create test cases for different parts of your codebase, and execute tests to ensure the functionality and reliability of my application. It also provided me with the skills needed to catch and address bugs early in the development process, leading to more robust and maintainable code since I was also fixing the code in the actual codebase while I was writing the tests for it. It gave me an understanding of advanced testing concepts like mocking, code coverage analysis, watching for test runner changes, etc. These skills will help me facilitate collaboration in a team, allowing me to be future-ready.