Lately I have been very interested in writing tests for my code. Last week I wrote a piece on how to test Ruby code using RSpec so I thought it would be fitting to look into testing JavaScript. Every time I work on something new I make it a point to unit test and practice writing better tests. One of the biggest benefits of writing tests for your code is that it forces you to think about how your code should behave in different cases. It is easy to start writing code and forget about weird edge cases. By writing tests we can write better quality code and make it much more easier for others to change the code in the future. For these reasons I am going to show how to get started writing tests with Jest. Jest is focused on simplicity so it will make it very easy for someone who has never written tests to get started.
I have created a couple of example JavaScript files that we can start testing. They each will contain a simple function. One of them is a greeting function that will take in a name and output a greeting and the other is a function that will clone an array and output it. This is what they will look like.
//greeting.js
function greeting(name) {
return \`Greetings ${name}!\`
}
module.exports = greeting
//cloneArray.js
function cloneArray(array) {
return \[...array\]
}
module.exports = cloneArray
Now let’s get setup first. Run these commands to generate a package.json and install Jest.
$ npm init -y
$ npm i --save-dev jest
Once everything is installed go into package.json and change the test script to “jest”.
//package.json
"scripts": {
"test": "jest"
},
Now every time you run “npm test” all of your tests will run and you will see whether your tests passed or not. Let’s get started writing some tests now. To get started all you have to do is create a new file and give it the exact same name as the file you want to test followed by “.test.js”. For example, if we want to test “greeting.js” we would create a file name “greeting.test.js”. Inside of this new file we need to import the function and create our test. For the greeting we expect the function to take a name and output the greeting with whatever name was chosen. This is what this test looks like.
const greeting = require('./greeting')
test('outputs a greeting with a whatever name is passed in', () => {
expect(greeting('Ray')).toBe('Greetings Ray!')
})
Ok so let’s go over this syntax. First we have the ‘test’ function which is our actual test block. For the first argument it takes a string that explains how the function is supposed to behave. The second argument is a function where we wrap the greeting function call inside the ‘expect’ function. Then we have ‘.toBe’. This is referred to as a matcher. There are many types of matchers and we will be taking a look at another type here but if you would like to look at a list here is the page to the Jest docs. This line of code can be read almost like plain English. We are expecting the output of the greeting function to be ‘Greetings Ray!’. When you run npm test you will see that the test passed!
PASS js/greeting.test.js
✓ outputs a greeting with a whatever name is passed in (2 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.558 s, estimated 1 s
Ran all test suites.
Now let’s write the test for our cloneArray function. This test will expect the output of the function to be a clone of the array that was passed in. This is what that will look like.
const cloneArray = require('./cloneArray')
test('properly clones an array', () => {
const array = \[1, 2, 3\]
expect(cloneArray(array)).toBe(array)
})
This time we are creating an array so that we can pass it into the function call. Alright so if you run your test you will notice that your test didn’t actually pass.
expect(received).toBe(expected) // Object.is equality
If it should pass with deep equality, replace "toBe" with "toStrictEqual"
Expected: \[1, 2, 3\]
Received: serializes to the same string
5 | test('properly clones an array', () => {
6 | const array = \[1, 2, 3\]
> 7 | expect(cloneArray(array)).toBe(array)
| ^
8 | // expect(cloneArray(array)).toEqual(array)
9 | // expect(cloneArray(array)).not.toBe(array)
10 | })
at Object.<anonymous> (js/cloneArray.test.js:7:29)
Test Suites: 1 failed, 1 passed, 2 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 0.743 s, estimated 1 s
Ran all test suites.
But why? Well this is the perfect time to explain what the ‘.toBe’ matcher does. What this is pretty much saying is that this matcher uses ‘Object.is’ to check for exact equality. We are getting an output that looks the same as what we are supposed to be getting but in reality it is not. Our function is creating a completely new array that references a completely different address in memory. So we need to introduce a different matcher. So let’s change our test to look like this.
const cloneArray = require('./cloneArray')
test('properly clones an array', () => {
const array = \[1, 2, 3\]
expect(cloneArray(array)).toEqual(array)
expect(cloneArray(array)).not.toBe(array)
})
The ‘.toEqual’ matcher checks the value instead. I also added a line that checks to see that the array that was inputted is not the same array as the one being outputted. That way we make sure that we are getting a clone of the array. This will get our test passing!
That is all you need to start testing. You can also use this testing framework for projects that use Typescript, React, Angular or other frameworks. Testing might seem unnecessary or complicated at first but it will make your code better. The programmers that write better tests also write better code. I hope that everyone who reads this gives it a try and has as much fun writing tests as I have. Happy coding! 😎
Top comments (1)
Hey there! I just read your post and I really liked it. However, I noticed that your code snippets seem a bit off. I thought I'd share a guide that might help you format them properly. Let me know if you have any questions or need further assistance. Keep up the great work!