TDD & Automated Testing in JavaScript using Jest
Tags: javascript, testing, beginners, tutorial
Published: 2020-11-04 23:06:42

TDD (Test Driven Development), automated testing, unit test, integration test, are some of the popular keywords you find in recent software development job descriptions/requirements, most especially JavaScript (react, react-native, nodejs) related jobs.

In this article, I will explain how to get started on writing automated test in JavaScript using **Jest**.

## JEST
As stated in their [official landing page](https://jestjs.io/)
>Jest is a delightful JavaScript Testing Framework with a focus on simplicity.

Jest is one of the most popular JavaScript testing framework and can be used to write automated test for almost everything JavaScript. It is easy to understand and get started with.

Before we dive into writing codes, I will explain some important concepts
### Automated Testing
>Automated testing or test automation is a method in software testing that makes use of special software tools to control the execution of tests and then compares actual test results with predicted or expected results.

Simply put it is a way of testing our code(software) in other to compare the actual test results with predicted or expected results without manually going through the code.

This help ensure our code is bug-free at all point as tests will fail if bug is introduce to the code at any point.

Automated testing can be broken into two main types namely: **unit tests** and **integration tests**.
>Unit tests simulate all possible outputs of a single function or component, given various inputs. They mock all external function calls in order to achieve a pure test of only that one function's logic.
Integration tests check to make sure that various functions or components are working together properly.

Now having understand these concepts, let dive in writing test codes.
 
##Set-Up
_You need to already have [nodeJs](https://nodejs.org/) installed/setup to follow along._

1. Create a new folder "test_master_class"
2. On your terminal cd into "test_master_class" and run the command `npm init`  to scaffold [package.json](https://nodejs.dev/learn/the-package-json-guide). _Enter "jest" when asked for "test command" during scaffolding._
3. Run `npm install --save-dev jest` to install jest as a dev dependency.
4. Finally open your folder in your favorite editor. 

![Alt vs code showing package.json](https://dev-to-uploads.s3.amazonaws.com/i/qzobej672798c6olun3p.png)

Now to write our first test code, which will be a very simple demo, inside your working folder, create a folder named "test" and inside the folder create a file named "index.test.js" and add the following code
{% gist https://gist.github.com/Dverybest/c532975ccf92b7eaf64f2471c9753844 %}
to run the test, on your terminal type `npm test`and press enter; and you would get the follow output
```
User@CharlesBest MINGW32 ~/Documents/MyJavaScript/test_master_class
$ npm test
> test_master_class@1.0.0 test C:\Users\User\Documents\MyJavaScript\test_master_class
> jest

PASS test/index.test.js
  test to see if test enviroment is properly set up
    √ test hello word (4 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        5.842 s
Ran all test suites.
```
From the code above in "index.test.js", **describe** is a global function from jest. It takes two main arguments, first a string that describes the tests to be contained in the describe block and the second argument is an an anonymous function containing the actual tests to be done. One of the main functions of the describe function is to group related test together. Similarly, **test**  is also a global function from jest, it contains the actual test to be done. Also **expect** is a global function from jest, it takes the actual test result and compare it to an expected output contained in matchers like **toBe**.

A comprehensive list of other matchers like toBe, toEqual, toBeTruthy, etc. and their functions can be found on the [documentation](https://jestjs.io/docs/en/using-matchers).

Now that we are clear with the basics of testing using jest, lets write unit and integration test for real scenarios.
## Getting Real
**Scenario**: Let's build a simple calculator system, this system should be able to increment and decrement a counter with a given number, get the current value of the counter after each operation and the time taken to perform each operation, we should be able to reset the value of the count.

**Solution**: if we are to follow TDD principles, we are expect to write test first before the actual code.

In our test folder create a file named "calculator_service.test.js"  and add the following code and run the test on the terminal using `npm test`
{% gist https://gist.github.com/Dverybest/f87ebdc7626c26624139c06a337197c8/af16770bf357dae4a103d4f44992e83997aba759 %}
we get the output below
```
PASS test/index.test.js (6.212 s)
FAIL test/calculator_service.test.js
  ● Test suite failed to run
    Cannot find module '../service/calculator' from 'test/calculator_service.test.js'
    > 1 | const {add,subtract,getTime} = require('../service/calculator');
        |                                ^
      2 |
      3 | describe('test to see if functions are defined', () => {
      4 |

      at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:306:11)
      at Object.<anonymous> (test/calculator_service.test.js:1:32)

Test Suites: 1 failed, 1 passed, 2 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        40.271 s
Ran all test suites.
npm ERR! Test failed.  See above for more details.

```
an error occurs making the test in "calculator_service.test.js" not to run and this is normal as "../service/calculator" we required does not exist yet. 

In the root folder, create a folder "service", inside the folder add a new file "calculator.js" and run the test again, this time we get the below output
```
PASS test/index.test.js
FAIL test/calculator_service.test.js
  ● test to see if functions are defined › test add function
    expect(received).not.toBeUndefined()
    Received: undefined
      4 |
      5 |     test("test add function", () => {
    > 6 |         expect(add).not.toBeUndefined()
        |                         ^
      7 |     })
      8 |
      9 |     test("test add function", () => {
      at Object.<anonymous> (test/calculator_service.test.js:6:25)
  ● test to see if functions are defined › test add function
    expect(received).not.toBeUndefined()
    Received: undefined
       8 |
       9 |     test("test add function", () => {
    > 10 |         expect(subtract).not.toBeUndefined()
         |                              ^
      11 |     })
      12 |
      13 |     test("test add function", () => {
      at Object.<anonymous> (test/calculator_service.test.js:10:30)
  ● test to see if functions are defined › test add function
    expect(received).not.toBeUndefined()
    Received: undefined
      12 |
      13 |     test("test add function", () => {
    > 14 |         expect(getTime).not.toBeUndefined()
         |                             ^
      15 |     })
      16 | });
      at Object.<anonymous> (test/calculator_service.test.js:14:29)

Test Suites: 1 failed, 1 passed, 2 total
Tests:       3 failed, 1 passed, 4 total
Snapshots:   0 total
Time:        10.71 s
Ran all test suites.
npm ERR! Test failed.  See above for more details.
```
This time our test ran successfully, but the test cases failed. From the output you can see what was expected and what was received.
Next, we create the expected functions by adding the following code to "../service/calculator.js" and run the test again.
{% gist https://gist.github.com/Dverybest/51e871ebd5ac948a8316cba01eda6e63 %}
this time, all the test passes as shown in the output below
```
$ npm test
> test_master_class@1.0.0 test C:\Users\User\Documents\MyJavaScript\test_master_class
> jest
PASS test/index.test.js (5.568 s)
PASS test/calculator_service.test.js (9.661 s)

Test Suites: 2 passed, 2 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        16.167 s
Ran all test suites.
```
We can add further test cases to "calculator_service.test" as much as required. Example test to check if the functions returns a correct output when given an input.
{% gist https://gist.github.com/Dverybest/f87ebdc7626c26624139c06a337197c8/e8180ccc5c9e64fba7e152dba8578b7971e93c4f %}
```
$ npm test
> test_master_class@1.0.0 test C:\Users\User\Documents\MyJavaScript\test_master_class
> jest
PASS test/index.test.js
PASS test/calculator_service.test.js

Test Suites: 2 passed, 2 total
Tests:       7 passed, 7 total
Snapshots:   0 total
Time:        9.401 s
Ran all test suites.
```
So far all test done so far are unit tests. Now lets write integration tests to test the functions coupled together. 

In our test folder, create a new file "calculator_controller.test.js" and add the following code
{% gist https://gist.github.com/Dverybest/53f130a8d25dec494e782b3d76616b9e %}
Next in the root folder, create a folder "controller", inside the folder add a new file "calculator.js" and add the following code then run the test again
{% gist https://gist.github.com/Dverybest/9a129bcc90ba2e1fa963e0e57ec4e4c5 %}
if everything goes well, you should have the below output
```
$ npm test
> test_master_class@1.0.0 test C:\Users\User\Documents\MyJavaScript\test_master_class
> jest

PASS test/index.test.js
PASS test/calculator_service.test.js
PASS test/calculator_controller.test.js

Test Suites: 3 passed, 3 total
Tests:       12 passed, 12 total
Snapshots:   0 total
Time:        6.974 s
Ran all test suites.
```
## Conclusion
In "calculator_controller.test.js", you many notice that I introduced two new functions **beforeAll** and **afterAll**, they are jest functions used to perform some operations before and after your test runs respectively.

TDD is all about writing "automated test" first, before writing codes that will pass the test; this helps ensure codes are broken into testable units which in turn reduce code duplication and bugs. 

To pull the whole code, goto [the github link](https://github.com/Dverybest/test_master_class)
I hope you find this article useful, if you like the content, feel free to stay in touch, follow me on [twitter](https://twitter.com/_DVERYBEST).