Writing Unit Tests
While creating our applications, testing is a critical aspect that ensures the correctness and reliability of your code. Testing, particularly unit testing, is all about writing additional code to verify if your functions are working as expected. One of the most popular testing libraries in the JavaScript ecosystem is Jest. This article will guide you through the process of writing a basic unit test with Jest.
Why Do We Need to Test Functions?
Unit tests allow developers to test individual units of code (usually functions) in isolation. If a unit test passes, you can be confident that the function is correctly doing what it was designed to do. This makes the codebase more robust and less prone to bugs.
Tests also serve as documentation. By reading the tests, other developers can understand what a function is supposed to do and what its output should be for a given input. Additionally, tests make refactoring safer and easier because you'll quickly find out if your changes have broken anything.
Setting Up Jest
Before you can write tests with Jest, you'll need to install it. Create a new project with NPM and then in install Jest (from the root folder) You can do this using npm (Node Package Manager) in your terminal:
npm install --save-dev jest
This command installs Jest as a devDependency, meaning it will not be included when your code is compiled for production.
Writing Your First Test with Jest
Now that you have Jest installed, let's write a "Hello, World!" test.
First, create a new JavaScript file for your function. Let's call it greeting.js
:
function greeting() {
return "Hello, World!";
}
module.exports = greeting;
Next, create another JavaScript file for your test. By convention, it should be named after the file it's testing with .test.
added before the file extension. So, let's call it greeting.test.js
:
const greeting = require('./greeting');
test('the function greeting should return "Hello, World!"', () => {
expect(greeting()).toBe("Hello, World!");
});
The test
function takes two arguments: a string description of the test and a callback function to run the test. Inside the callback function, we use expect
to assert what the result should be. The toBe
method checks that the output of greeting()
is exactly "Hello, World!"
.
Running the Test
To run your test, you'll use the jest
command in your terminal:
npx jest
If everything is set up correctly, Jest will find and run your test, then report back whether it passed or failed.
In addition to the test
function, Jest provides describe
and it
functions to help structure your tests in a more readable and organized manner. They allow you to group tests and create more descriptive test names, making your tests function both as checks for code correctness and as effective documentation.
Why Use describe
and it
?
The describe
function allows you to group related tests together, often those tests which target the same function or a specific feature of your application. This helps to keep your test suite organized, making it easier for other developers (and you, in the future) to understand the purpose and coverage of your tests.
The it
function is equivalent to test
. It's simply an alias that, when combined with describe
, can lead to more readable test output.
By using describe
and it
, you can write your tests in a way that they read almost like sentences, describing the behavior of your functions in clear terms.
Example: Testing greeting
Function with describe
and it
Let's rework our previous greeting
function tests using describe
and it
. Suppose we want to test not just the basic functionality, but also how greeting
behaves when given an argument.
First, update your greeting.js
:
function greeting(name) {
if (name) {
return `Hello, ${name}!`;
}
return "Hello, World!";
}
module.exports = greeting;
The function now takes an optional name
parameter. If name
is provided, it will greet the named individual; otherwise, it will default to "Hello, World!"
.
Next, rewrite the tests in greeting.test.js
:
const greeting = require('./greeting');
describe('greeting', () => {
it('returns "Hello, World!" when called without arguments', () => {
expect(greeting()).toBe("Hello, World!");
});
it('greets the provided name when called with a string argument', () => {
expect(greeting("Jest")).toBe("Hello, Jest!");
});
});
In the test file, we now have a describe
block for greeting
that groups two tests together. The it
function is used instead of test
, and the combination of describe
and it
leads to more readable test names.
You also might be curious about what describe
and it
individually do. They basically provide formatting to the test. Try to write a test with only describe
(without it
) or vice versa and you will see how different the format in the console is!