Beginner’s Guide To Unit Testing with Mocha and Chai
Prerequisites
- Node.js 14+ installed – https://nodejs.org/en/download/
- Mac or Linux terminal
- Familiarity with using the command line / terminal
- Experience writing Javascript
Introduction
The goal of this article is to teach you how to setup and test JavaScript code using Mocha and Chai. You will learn what unit testing is and why it is important. Next, you will setup a testing environment where you can write tests and get immediate feedback of the test results. Finally, you will walk through writing a unit test step by step.
What is Unit Testing?
Unit testing can be described by thinking about two types of code. The first type of code is called production code. Production code runs applications in the real world and it directly affects the people that use software applications. The second type of code is test code. The purpose of test code is to verify that the production code works. The test code is written for you, the developer (and your team), to help you 1) understand how the code works and 2) to prevent breaking changes that commonly occur while refactoring and/or adding new features.
The practice of writing unit tests is all about making sure small “units” of the production code work correctly. In summary, you write unit tests to make sure that your code works.
Why are unit tests important?
Unit tests are very important for many reasons. Here is a short list of reasons why writing unit tests are so important:
- Unit tests give you confidence in your code (proof your code works)
- It is easier to work with others (reading tests is easier than reading code)
- Forces you to write smaller, more manageable units of code
- Proof that your codes works especially after refactoring
- Testing is required by major companies that would hire you
Now that you see how unit testing can help you, let’s get our test suite setup!
If you have a small app that only you use and code, maybe you don’t need tests. Imagine now that your application is responsible for providing the accurate regulatory status for a chemical or substance that will be added to food. Or maybe your software calculates taxes across different currencies and tax rates. A little more important!
How To Setup Mocha and Chai to Begin JavaScript Unit Testing?
We will be using Mocha and Chai for our unit testing.
Jest has become a very popular tool for testing, however, mocha and chai are very simple tools that don’t try to do too much. Mocha and Chai are also more performant.
What are these tools (Mocha & Chai)?
Mocha is the “test runner”. Mocha’s job is to find our tests in our test files and execute them. Chai is an assertion library. With Chai you can make assertions (declarations or positive statements) about what our code should do and not do.
Follow the steps below to get your test environment setup. Alternatively, you can clone this repository (https://github.com/rbk/testing-javascript) and skip to Example Unit Test: Detect Reoccurring Language section.
Step by step setup from the terminal:
mkdir YOUR-FOLDER
cd YOUR-FOLDER
npm init -y
npm i mocha chai --save-dev
Once everything is installed, we can setup the tests to run automatically when we make changes to the files.
In your package.json, under “scripts”, you should see this line:
"test": "echo \\"Error: no test specified\\" && exit 1"
Replace with this line:
"test": "mocha"
Mocha, our test runner, will look for tests with by searching for files ending with .spec.js
and execute them in a special runtime that has access to it
and describe
that we will see later.
To try this out, in the terminal enter npm test
. You should see output similar to this:
0 passing (3ms)
If not, make sure you have the correct file structure and that mocha is installed. This output shows us that we have 0 tests passing.
npm test
now calls Mocha, our test runner!
If you are not very familiar with npm,
npm test
is short fornpm run test
. If you want to see all the available commands in your package.json (all the commands are in the scripts section) just runnpm run
. I highly recommend reading about NPM here: https://docs.npmjs.com/cli/v7/commands
Also in the package.json file, under the previous line you added, add the following line:
"test:watch": "mocha --watch"
With this added to your package.json, you can now enter npm run test:watch
in the terminal and Mocha will rerun the tests when the code changes. Pretty nifty! Now that your test environment is setup, let’s write some tests!
How to write unit tests for JavaScript functions?
Writing good tests challenges you to be creative. Your tests should help you understand how the code should work and equally valuable, it should help you understand how it should NOT work.
First, let’s create the files we need. In the terminal run the following commands to create the necessary files and folders:
touch detect-reoccurring.js
– create an empty file called detect-reoccurring.jsmkdir test
– create a test directory called testtouch test/detect-reoccurring.spec.js
– create an empty file called detect-reoccurring.spec.js
By default, Mocha will look for all files ending in spec.js
and then execute the tests inside those files.
Example Unit Test: Detect Reoccurring Language
In this example, you will write a function that will take some text and determine if the text includes language that describes something that should happen at an interval. For example, in the text. “Write code every day”, we want to return true because the words “every day” imply that it is a reoccurring action.
In our test file ‘./test/detect-reoccurring.spec.js’, the first thing we want to do is require our assertion library Chai. This library will help us write assertions. Assertions are statement about how the code should work.
const {expect} = require('chai');
Next, we need to include the function we are testing.
const {detectReoccurring} = require('../detect-reoccurring');
This function doesn’t exist yet! Let’s define and export the function in detect-reoccurring.js:
const detectReoccurring = (text) => {
// Logic for production code
}
module.exports = {
detectReoccurring
}
Mocha – Describe
Using describe
, a function that is imported automatically via Mocha, we can describe a group of tests for our function:
describe('detect reoccurring language', () => {
// Tests go here
});
Describe is not a test, it is just a way to organize/group testes together. All our tests for this function will go within the function of the describe.
Mocha – It
Using it
, another function imported by Mocha, we will write a test. The first argument of this function is where out statement of something we expect our code to do. In this first test, we want our function to return false if no reoccurring language is detected:
describe('detect reoccurring language', () => {
it('should return false if no reoccurring language is detected', () => {
// Assertions or statements of how the code should work
});
});
If you have the mocha test running watching, you should see 1 passing test! We did it! All done. Not so fast, we need to make an assertion about our code inside the it statement.
Inside the it function, you call the production code. Using the result, you can make an assertion about what should have happened. In the following code, we expect the result to be false because the string doesn’t include language we consider reoccurring.
describe('detect reoccurring language', () => {
it('should return false if no reoccurring language is detected', () => {
// String to test
const text = 'this is a string'
// Call the function we are testing and save the result
const result = detectReoccurring(text);
// Use Chai expect to assert that the result should be false
expect(result).to.be.false;
});
});
In the same describe function, let’s also add a test for when our function should return true. In this test, we expect our function to return true because of the word “daily” is in the text:
it('should return true if reoccurring language is detected', () => {
const text = 'daily coding exercise'
const result = detectReoccurring(text);
expect(result).to.be.true;
});
In your terminal, run the tests. You should have two failing tests. Progress! The tests fail because we haven’t written the code to detect reoccurring language. Given the tests we wrote, the following code this make both tests pass. Update detectReoccurring function so that when the text includes the word “daily” it returns true, else, return false
const detectReoccurring = (text) => {
if (text.match('daily')) {
return true
}
return false;
}
Run the tests again and you now we have two passing tests!
Conclusion
In this article we looked at getting started unit testing basic unit testing for javascript. This knowledge will give you more confidence in your code. We setup npm with mocha as our test runner and did TDD on a simple concept function to detect reoccurring language.
I hope this has helped you understand how to write unite tests for your code. My next article will include more advanced testing concepts that you will definitely need for writing production code in the real world.
Testing Tips
- Make sure your test breaks in the way you expect! After you write a test and see it pass, change the production code to see it break. This will prove that your test is testing what you think it is.
- Write your tests so that they handle as many different scenarios as possible. For example, write a test for the error and success of a function.
- If you remove an important line of code, but the tests still pass, write more tests. Tests should prevent removal of important logic.
- Keep functions small to make them easier to test.
References and Docs
- https://docs.python-guide.org/writing/tests/
- https://www.chaijs.com/
- https://mochajs.org/
- http://nodejs.org/