Testing is carried out when developing applications to ensure the code works as expected. There are different types of testing and the common ones include unit testing, integration testing, and functional testing.
Test-driven development (TDD)
, is a software development process that is very common when developing applications and emphasizes testing before the actual code is implemented.
This approach starts with failing tests, the developer goes ahead to implement the actual code to ensure the test passes. Once this is done, new failing test cases are added and the process repeats until the system is complete.
We should always ensure that our code has test cases to prevent bugs in our applications. To support testing in our project, we will use a common testing framework known as Jest. Jest is a testing framework that makes it very easy to test our applications.
The framework is fast and safe and provides features such as code coverage
, mocking
objects, and causes of exceptions
. In this tutorial, we will learn how to test our code in JavaScript and the main focus will be on unit testing using the Jest framework.
How to install the Jest
framework
Open WebStorm development environment and select File > New > Project
. On the window that opens, select Node.js
and enter the project name as javascript-testing
on the Location
section. Finally, press the Create
button to generate a node application.
To install Jest, select View > Tools Window > Terminal
to open a terminal window. On the terminal window that opens at the bottom of the IDE, copy and paste the following command and press enter button on your keyboard.
npm install --save-dev jest
This command is for those users who are using the npm
package manager. If you are using the yarn
package manager, use the following command.
yarn add --dev jest
Creating a sample test case
Create the folder structure com/main/app
under the root folder. Create a file named app.js
under the app
folder. This file will contain all the code that will be tested using the Jest framework. Copy and paste the following code into the file.
exports.summation = (a, b) => {
return a + b;
}
This code creates an arrow function that returns the result of adding two numbers. To test whether this code returns the expected result, create the folder structure com/test/app
. Create a file named mathers.test.js
under the app
folder.
This file will contain test cases that match our code with an expected result. Copy and paste the following code into the file.
const {summation} = require('../../main/app/app')
test(`adds 1 + 2 to equal 3`, () => {
expect(summation(1,2)).toBe(3);
})
In this code the require()
method imports our summation()
function by referencing the function name summation
as shown above.
We usually perform testing inside the test()
function which is provided by the Jest framework. Inside the test function, we call expect()
and pass our summation()
containing the arguments 1
and 2
as the argument of the function.
We usually use the expect()
function when we want to test a value, and we rarely use it by itself. The toBe()
method checks for strict equality but should not be used with floating point values. To run the above code, use the following command.
npm test matchers.test.js
output:
> javascript-testing@1.0.0 test
> jest "matchers.test.js"
PASS com/test/app/matchers.test.js
✓ add 1 and 2 to get 3 (3 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.494 s, estimated 2 s
Ran all test suites matching /matchers.test.js/i.
Since our code only had one test, we can see that the one test passed successfully, and also the time taken to execute the test.
Checking the value of an object
Copy and paste the following code into the app.js
file after the previous example.
test('checking object assignment', () => {
const user = {firstName: 'john'};
user['lastName'] = 'doe';
expect(user).toEqual({firstName: 'john', lastName: 'doe'})
})
This example is similar to the one we covered above but the only difference is that it uses the toEqual()
function.
The toEqual()
function is used to check whether two functions have the same values, and this is done by checking each of the fields recursively instead of using the object identity to check the value of the objects.
Run the above code using the same command and note that now we have to test cases that have passed including this example.
> javascript-testing@1.0.0 test
> jest "matchers.test.js"
PASS com/test/app/matchers.test.js
✓ add 1 and 2 to get 3 (3 ms)
✓ checking object assignment (1 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.609 s, estimated 1 s
Ran all test suites matching /matchers.test.js/i.
Checking for a negation of a matcher
Copy and paste the following code into the app.js
file after the previous example.
test('positive numbers cannot be zero', () => {
for (let innerLoop = 1; innerLoop < 10; innerLoop++){
for (let outerLoop = 1; outerLoop < 10; outerLoop++){
expect(innerLoop + outerLoop).not.toBe(0);
}
}
})
In this test()
function, we create two nested loops and pass the sum of the outer loop and inner loop into the expect()
function.
Since all the values in the loop in the loops are greater than 0
, we know that we cannot get a sum that is less than 0
.
To check this, we invoke .not.toBe()
on expect()
function and pass an argument of 0
to the toBe()
method.
Run the above code using the same command and observe that we now have three test cases that have passed including this example.
> javascript-testing@1.0.0 test
> jest "matchers.test.js"
PASS com/test/app/matchers.test.js
✓ add 1 and 2 to get 3 (3 ms)
✓ checking object assignment (2 ms)
✓ Positive numbers cannot be zero (18 ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 0.64 s, estimated 1 s
Ran all test suites matching /matchers.test.js/i.
Testing for truthiness
Truthiness involves using helper functions to check the expected result on undefined
, null
, and false
. Copy and paste the following code into the app.js
file after the previous example.
test('checking truthiness using null', () => {
const element = null;
expect(element).toBeNull();
expect(element).toBeDefined();
expect(element).not.toBeUndefined();
expect(element).not.toBeTruthy();
expect(element).toBeFalsy();
})
The toBeNull()
method is used to check if the expected result is null
. Note that this method is the same as toBe()
but the error messages are nicer while using the toBeNull()
.
The toBeDefined()
method is used to check whether the expected result is not undefined, and since we have null it will pass.
The toBeUndefined()
method is used to check whether the expected result is undefined, and since we have null, we use the .not
to negate the matcher and the test passes.
The toBeTruthy()
method is used to check whether the expected result is true, and since we have null which evaluates to false, we use the .not
to indicate that the expected result is not true.
The toBeFalsy()
method is used to check whether the expected result is false, and since null evaluates to false, the test passes without any changes.
Run the above example using the same command and observe that we now have four test cases that have passed.
> javascript-testing@1.0.0 test
> jest "matchers.test.js"
PASS com/test/app/matchers.test.js
✓ add 1 and 2 to get 3 (3 ms)
✓ checking object assignment (2 ms)
✓ Positive numbers cannot be zero (18 ms)
✓ checking truthiness using null (2 ms)
Test Suites: 1 passed, 1 total
Tests: 4 passed, 4 total
Snapshots: 0 total
Time: 0.633 s, estimated 1 s
Ran all test suites matching /matchers.test.js/i.
Testing for expected results using numbers
Copy and paste the following code into the app.js
file after the previous example.
test('adding two numbers', () => {
const result = 2 + 2
expect(result).toBeGreaterThan(3);
expect(result).toBeGreaterThanOrEqual(3.5);
expect(result).toBeLessThan(5);
expect(result).toBeLessThanOrEqual(4.5);
expect(result).toBe(4);
expect(result).toEqual(4);
})
I know by now you have gotten a basic understanding of how testing works. The method named are self-explanatory and this makes testing very easy.
For example, the toBeGreaterThan()
method checks if the expected result on a float or a big integer is greater than the provided value.
If you are familiar with comparison operators it is quite easy to understand what the remaining methods do. The only precaution here is that the toBe()
method should not be used with floating point numbers. The toBeCloseTo()
method should be used in case of such situations.
Run the above code using the same command and observe that now we have five test cases that have passed.
> javascript-testing@1.0.0 test
> jest "matchers.test.js"
PASS com/test/app/matchers.test.js
✓ add 1 and 2 to get 3 (3 ms)
✓ checking object assignment (2 ms)
✓ Positive numbers cannot be zero (25 ms)
✓ checking truthiness using null (2 ms)
✓ adding two numbers (4 ms)
Test Suites: 1 passed, 1 total
Tests: 5 passed, 5 total
Snapshots: 0 total
Time: 0.652 s, estimated 1 s
Ran all test suites matching /matchers.test.js/i.
Checking strings against regular expressions
Copy and paste the following code into the app.js
file after the previous example.
test('email format has no .com', () => {
expect('@gmail.net').not.toMatch(/com/);
})
test('email format has .com', () => {
expect('@gmail.com').toMatch(/com/);
})
To check for expected results in a string using a regular expression, we usually use the toMatch()
method as shown above.
The first test()
function in the above example checks that the provided string does not match the provided regular expression while the second function checks that the provided string matches the provided regular expression.
Run the above code using the same command and observe that we now have seven test cases that have passed.
> javascript-testing@1.0.0 test
> jest "matchers.test.js"
PASS com/test/app/matchers.test.js
✓ add 1 and 2 to get 3 (3 ms)
✓ checking object assignment (2 ms)
✓ Positive numbers cannot be zero (16 ms)
✓ checking truthiness using null (2 ms)
✓ adding two numbers (1 ms)
✓ email format has no .com (1 ms)
✓ email format has .com (1 ms)
Test Suites: 1 passed, 1 total
Tests: 7 passed, 7 total
Snapshots: 0 total
Time: 0.53 s, estimated 1 s
Ran all test suites matching /matchers.test.js/i.
Conclusion
Up to this section, we have only covered the basics of testing in JavaScript. If you want to read more about testing using Jest
, visit the Jest documentation which covers more testing concepts in detail.