JavaScript Unit Testing
Full disclosure: this document is based on the course "Learn JavaScript Unit Testing" at Codecademy.com
#
Why Test?When writing your app using JavaScript, it is often easier and more convenient to assume that the lines of code you just wrote are correct. After all, you wrote them. Unfortunately, we are human and we err. Let's catch these faux paus before they affect your teammates' work. Enter, unit tests.
For the uninitiated, unit tests (and automated tests in general) are better than manual testing because it is:
- faster
- more reliable
- maintainable
A typical workflow should consist of something like the following:
- Write code and corresponding tests
- Enter a command into a terminal to run tests
- If all the tests pass, Congratulations! Otherwise, go back to step 1 and review your code
Tests as documentation provide what many other forms cannot: both human-readable text to describe the application and machine-executable code to confirm the app works as described.
Quick test, see if you can understand what is going on with the following code:
Answer: it accepts the customer name
If you're still reading, great. You know and understand the importance of testing while developing.
Your test code should live in close proximity to your development code, and it should be easy to recognize which tests correspond to what development code. i.e. index.js
and index-test.js
.
#
How to Make an Effective Test SuiteAny change to your test suite should increase its value in at least one of these characteristics
- Fast
- Complete
- Reliable
- Isolated
- Maintainable
- Expressive (i.e. easy to read)
#
How to Test in JavaScript (spoiler: use Mocha)Required technologies:
JavaScript
Node.js
Mocha
test framework
#
How to install Mocha:#
How to run Mocha:- you can call it directly from node_modules
- add a script to package.json. In the
scripts
object, set the value of"test"
tomocha
.
Now you can call Mocha with the following command:
describe
and it
blocks in Mocha#
In Mocha, tests are grouped using the describe
function and define tests using the it
function. Both functions accept two parameters: a descriptive string and a callback function.
Convention is the following:
- nest
describe
blocks to resemble the structure of your implementation code and write individual tests init
blocks
For example, this is how you would test a Math.max()
function:
assert
in Mocha#
To write the actual tests, we use assert.ok
method provided by Node.js.
Import assert
at the top of your file with something like
If an argument passed to assert.ok()
is false, an AssertionError
is thrown, and that error is logged to the console.
#
Proper Setup of Tests in MochaI'm going to include a quick caveat before this section: any code that works, will work (obviously). I will define best practice of how you should set up your tests for consistent, reliable, readable and maintainable code.
The set up of your tests should ideally follow into a pattern of three distinct phases:
- Setup: where you create objects, variables, and set conditions that you will use to test
- Exercise: execute the functionality you are testing
- Verify: use
assert
to check expectations against the result of the exercise (phase 2).
Below is an ideal test using the aforementioned pattern:
Remember that some of your tests may affect the results of others if you do not properly isolate them. In order to isolate your tests, a fourth stage may be necessary: Teardown. As it sounds, the Teardown phase is implemented to reset any conditions that were changed during the test before the next test runs.
When to use the fourth stage (not comprehensive):
- altering files and directory structure
- changing read/write permissions on a file
- editing records in a database
You do not need to always use the fourth stage in the mentioned scenarios if no conditions exist that need to be reset.
Here is a sample that is ideal for including this fourth stage:
The fs.unlinkSync(path)
is used to make sure the message.txt
is deleted (it is written each time the test runs as a consequence of the fs.appendFileSync
function).
#
How to Make an Effective Test SuiteAny change to your test suite should increase its value in at least one of these characteristics
- Fast
- Complete
- Reliable
- Isolated
- Maintainable
- Expressive (i.e. easy to read)
#
How to Test in JavaScript (spoiler: use Mocha)Required technologies:
JavaScript
Node.js
Mocha
test framework
#
How to install Mocha:#
How to run Mocha:- you can call it directly from node_modules
- add a script to package.json. In the
scripts
object, set the value of"test"
tomocha
.
Now you can call Mocha with the following command:
describe
and it
blocks in Mocha#
In Mocha, tests are grouped using the describe
function and define tests using the it
function. Both functions accept two parameters: a descriptive string and a callback function.
Convention is the following:
- nest
describe
blocks to resemble the structure of your implementation code and write individual tests init
blocks
For example, this is how you would test a Math.max()
function:
assert
in Mocha#
To write the actual tests, we use assert.ok
method provided by Node.js.
Import assert
at the top of your file with something like
If an argument passed to assert.ok()
is false, an AssertionError
is thrown, and that error is logged to the console.
#
Proper Setup of Tests in MochaI'm going to include a quick caveat before this section: any code that works, will work (obviously). I will define best practice of how you should set up your tests for consistent, reliable, readable and maintainable code.
The set up of your tests should ideally follow into a pattern of three distinct phases:
- Setup: where you create objects, variables, and set conditions that you will use to test
- Exercise: execute the functionality you are testing
- Verify: use
assert
to check expectations against the result of the exercise (phase 2).
Below is an ideal test using the aforementioned pattern:
Remember that some of your tests may affect the results of others if you do not properly isolate them. In order to isolate your tests, a fourth stage may be necessary: Teardown. As it sounds, the Teardown phase is implemented to reset any conditions that were changed during the test before the next test runs.
When to use the fourth stage (not comprehensive):
- altering files and directory structure
- changing read/write permissions on a file
- editing records in a database
You do not need to always use the fourth stage in the mentioned scenarios if no conditions exist that need to be reset.
Here is a sample that is ideal for including this fourth stage:
The fs.unlinkSync(path)
is used to make sure the message.txt
is deleted (it is written each time the test runs as a consequence of the fs.appendFileSync
function).
Caveat: using the fourth phase makes your code isolated, but not reliable. In order to make it reliable, use Mocha's hooks (code that is executed when a certain event happens). In Mocha, a hook is written within a describe
block.
Other hooks in Mocha are before()
, beforeEach()
, after()
.
For the completionists, here is the code from the fourth stage, done properly:
.....This is a work in progress... Feel free to edit as you see fit!...