TDD & Automated Testing in JavaScript using Jest
Charles Best
Posted on November 4, 2020
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
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 installed/setup to follow along.
- Create a new folder "test_master_class"
- On your terminal cd into "test_master_class" and run the command
npm init
to scaffold package.json. Enter "jest" when asked for "test command" during scaffolding. - Run
npm install --save-dev jest
to install jest as a dev dependency. - Finally open your folder in your favorite editor.
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
to run the test, on your terminal type
npm test
and press enter; and you would get the follow outputUser@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.
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
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.
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.
$ 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
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
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
I hope you find this article useful, if you like the content, feel free to stay in touch, follow me on twitter.
Posted on November 4, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.