Bootcamp
Search…
9.1: Testing

Introduction

Testing generally describes an automated system that guards against bugs in an application by defining test cases- behaviour and values that are expected from an application given a well-defined input.
There are many systems and methodologies to define the inputs and the system, we will be mostly focusing around unit tests, which are the most low level. Unit tests work with some well-defined module within the code that can be written and tested in isolation from the rest of the system.
The following are the 3 most common categories of tests.
  1. 1.
    Unit tests: Testing individual or small groups of functions
  2. 2.
    Integration tests: Testing API contracts
  3. 3.
    End-to-end tests: Replicating the user experience

Testing in Industry

Testing as part of a company's normal development workflow is a defacto standard in the industry. It is considered a best-practice to have at least some testing (like a smoke test) in place in most web applications so that new bugs are not introduced and that old features don't break. (It is not necessarily a standard in other industries/fields such as computer graphics, mobile applications or Machine Learning).
A standard workflow setup is to run testing as part of a Continuous Integration system, a system that runs tests and deploys the application automatically or at the click of a single button.
Brittle in this case refers to the idea that if you add any kind of code to a project it will have errors and need to be maintained. Unit tests run against some underlying logic of the app and so are less likely to need to change. Automatic tests need to be updated in many more situations, such as CSS changes (because the automation needs to know where to click) that are not related to the core app logic.
It is generally agreed that the volume of tests should be weighted towards easy-to-maintain unit tests.

Javascript Unit Testing

We will be using two tightly coupled libraries to run our tests: Mocha and Chai.

Mocha

Mocha is a "testing framework" which basically means the command line command that we run in order to kick off the tests we write. Mocha also provides wrapper functions that will run each "unit" of our tests sequentially and separately.

Chai

Chai is an assertion library, which means that the Chai functions we run are to verify values and states in our code.

Setup

Let's get a simple test running with a bare-bones Mocha and Chai setup.
Clone the base repo and install the libraries.
1
git clone https://github.com/rocketacademy/base-node-bootcamp.git mocha
Copied!
1
cd mocha
Copied!
1
npm install mocha chai
Copied!
Mocha expects a directory called test that holds the test files.
1
mkdir test
Copied!
For this example we'll create a simple calculator to test.
1
touch calculator.js
Copied!

calculator.js

1
export default function add(a, b) {
2
return a + b;
3
}
Copied!
Because we are doing unit testing, we are creating a library module to use as in this index.js below.

index.js

1
import { add } from './calculator.js';
2
​
3
console.log(add(2, 3));
Copied!
However, we won't be testing any functionalities inside the index.js itself. We'll only be running code in the caluclator.js file.
Now let's create the file that will run our tests.

test/calculator.js

1
import { expect } from 'chai';
2
​
3
import { add } from '../calculator.js';
4
​
5
describe('Calculator', function () {
6
describe('Basic Operations', function () {
7
it('Adds two numbers', function () {
8
const result = add(2, 2);
9
expect(result).to.equal(4);
10
});
11
});
12
});
Copied!
To run the tests, make a change to the package.json:
1
{
2
"name": "base-node-swe1",
3
"version": "1.0.0",
4
"description": "# basic-node-swe1",
5
"main": "index.js",
6
"scripts": {
7
"test": "mocha"
8
},
9
"repository": {
10
"type": "git",
11
"url": "git+https://github.com/rocketacademy/base-node-swe1.git"
12
},
13
"keywords": [],
14
"author": "",
15
"type": "module",
16
"license": "ISC",
17
"bugs": {
18
"url": "https://github.com/rocketacademy/base-node-swe1/issues"
19
},
20
"homepage": "https://github.com/rocketacademy/base-node-swe1#readme",
21
"dependencies": {
22
"chai": "^4.3.3",
23
"mocha": "^8.3.1"
24
},
25
"devDependencies": {
26
"eslint": "^7.12.1",
27
"eslint-config-airbnb-base": "^14.2.0",
28
"eslint-plugin-import": "^2.22.1",
29
"nodemon": "^2.0.6"
30
}
31
}
Copied!
On line 7 we changed the test key so that Mocha will run:
1
npm run test
Copied!
We should then see output like this:
Let's talk about the test code and the test run output.

Describe

On line 5 & 6 of test/calculator.js we are calling the describe function. This function segregates the different tests and formats the output according to what was called inside these functions.

It

it is the smallest piece of a unit test suite. The first argument to it is a string that describes what this code is testing.
1
it('Adds two numbers', function () {
2
const result = add(2, 2);
3
expect(result).to.equal(4);
4
});
Copied!

Expect

Expect is the code that actually evaluates that a result is correct. In this case we are checking for the number 4. There are many other kinds of comparisons besides to.equal.

Further Reading

  1. 1.
    ​Mocha website​
  2. 2.
    ​Chai documentation​
  3. 3.

Exercises

  1. 1.
    Run the above code
  2. 2.
    Run the test to see the output
  3. 3.
    Break the test on purpose to see what a failing test looks like
  4. 4.
    Add a multiply function to the calculator and write a test for it