Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
July 24, 2021 11:39 am GMT

How to write unit tests in JavaScript with Jest

Unit testing is an important and often overlooked part of the development process. It is considered boring by many, and being traditionally difficult to properly set up earned it a poor reputation early on. The benefits of shipping quality code certainly outweigh any negatives, but how does one find the time and muster the effort to start writing unit tests?

Lucky for us, writing unit tests in JavaScript has never been faster, easier, and arguably more fun thanks to Jest.

Jest is a feature-rich JavaScript testing framework that aims to bring testing to the masses. It's near-zero configuration approach makes it simple to set up, and a familiar API makes writing tests fairly straightforward.

This article will provide a brief introduction into Jest and the concepts behind unit testing. We will learn how to install Jest, write test suites with test cases and fixtures, and run tests both with and without coverage reports.

We will assume that we're testing a module containing a simple function behaving as a validation rule. The rule checks whether the validated value is an integer number. For example:

// isInteger.jsmodule.exports = (value) => !isNaN(parseInt(value, 10));

This implementation is naive and faulty on purpose. We want to see what our tests will teach us about the flaws in our code by passing and failing test cases. Fixing the implementation is not covered by this post, but feel free to play with it as we move through the article.

Read on to find out more!

What is a unit test?

A unit test is an automated test of a unit of source code. A unit test asserts if the unit's behaviour matches expectations.

A unit is usually a line of code, function, or class. There is no strict definition of what makes up a unit, but it's common to start with whatever seems "smallest".

Units that have no dependencies are called isolated (solitary) units. Units that have dependencies are called sociable units.

Solitary units are easy to test, but sociable units are more difficult. The output of a sociable unit depends on other units of code - if other units fail, the tested unit fails as well. This created two unit test styles: sociable unit tests and solitary unit tests.

Sociable unit tests fail if the dependencies of a sociable unit are also failing. The tested unit is not supposed to work if it's dependencies don't work, so a failing test in this case is a good sign.

Solitary unit tests isolate sociable units by creating mock implementations of their dependencies. Mocks control how dependencies behave during tests, making sociable units predictable to test.

No matter the unit test style, the goal of unit testing remains the same - to ensure that individual parts of the program are working correctly as expected.

What is Jest?

Jest is a JavaScript testing framework designed to make testing as easy as possible. It provides all the essential tools for running tests, making assertions, mocking implementations, and more in a single package.

Before Jest, the JavaScript ecosystem relied on several different tools and frameworks to give developers a way to write and run tests. Configuring these tools was rarely simple and easy. Jest aims to fix that by using sensible default configurations that work "out of the box", with little to no additional configuration required in most cases.

Jest is currently one of the most popular testing technology choices, consistently earning high satisfaction marks in the State of JS developer survey since 2017. It's the reliable choice for testing JavaScript projects.

Note
Jest also supports TypeScript via Babel.

How to install Jest?

Install the jest package (and optional typings) to a new or existing project's package.json file using your package manager of choice:

# For NPM usersnpm install --save-dev jest @types/jest# Yarn usersyarn add --dev jest @types/jest

That's it! We're now ready to run tests with Jest.

Note
It's good practice to install Jest and any other testing tools as development dependencies. This speeds up installation in environments where only dependencies required for the project to build and run are installed.

How to run tests with Jest?

To run tests with Jest call the jest command inside the root of the project folder.

We will update the project's package.json with a test script that calls the jest command for us:

{    // ... package.json contents    "scripts": {        // ... existing scripts        "test": "jest"    }}

We can now run the newly created test script:

# NPM usersnpm run test# Yarn usersyarn run test

If everything is set up correctly Jest will give us the results of any tests it found and ran.

Note
Jest exits with status code 1 when a test case fails. Seeing npm ERR! errors in the console is expected in this case.

How to create a test with Jest?

To create a test for use with Jest we create a *.spec.js or *.test.js file that will contain our test cases.

Note
Jest is configured by default to look for .js, .jsx, .ts and .tsx files inside of __tests__ folders, as well as any files with a suffix of .test or .spec (this includes files called test or spec).

Since isInteger.js is the name of the module we're testing, we will write our tests in an isInteger.spec.js file created in the same folder as the module:

// isInteger.spec.jstest("Sanity check", () => {    expect(true).toBe(true);});

Note
Whether you choose to write tests inside a dedicated folder or right next to your modules, there is no right or wrong way to structure tests inside a project. Jest is flexible enough to work with most project architectures without configuration.

The test's description is "Sanity check". Sanity checks are basic tests to ensure the system behaves rationally. The test will assert that we expect the value true to be true.

Run the test and if it passes everything is set up correctly.

Congratulations! We just wrote our first test!

How to write a test case in Jest?

To write a test case we first define the outcomes that we must validate to ensure that the system is working correctly.

The isInteger.js module is a function that takes one parameter and returns true if the parameter is an integer value or false if it isn't. We can create two test cases from that definition:

  1. isInteger() passes for integer value;
  2. isInteger() fails for non-integer value.

To create a test case in Jest we use the test() function. It takes a test name string and handler function as the first two arguments.

Note
The test() function can also be called under the alias - it(). Choose one over the other depending on readability or personal preference.

Tests are based on assertions. Assertions are made up of expectations and matchers. The simplest and most common assertion expects the tested value to match a specific value.

An expectation is created with the expect() function. It returns an object of matcher methods with which we assert something expected about the tested value. The matcher method toBe() checks if the expectation matches a given value.

In our tests, we can expect isInteger() to be true for the integer value 1, and false for the non-integer value 1.23.

// isInteger.spec.jsconst isInteger = require("./isInteger");test("isInteger passes for integer value", () => {    expect(isInteger(1)).toBe(true);});test("isInteger fails for non-integer value", () => {    expect(isInteger(1.23)).toBe(false);});

Running Jest should now give us a report on which tests pass, and which tests fail.

How to use fixtures in Jest?

To use fixtures in Jest we can use the test.each() function. It performs a test for each fixture in an array of fixtures.

Fixtures are data representing conditions - such function arguments and return values - under which the unit test is performed. Using fixtures is a quick and easy way to assert that a unit's behaviour matches expectations under different conditions without having to write multiple tests.

In Jest, a fixture can be a single value or an array of values. The fixture is available in the test handler function through parameters. The value or values of a fixture can be injected in the description through printf formatting.

// isInteger.spec.jsconst isInteger = require("./isInteger");const integerNumbers = [-1, 0, 1];test.each(integerNumbers)(    "isInteger passes for integer value %j",    (fixture) => expect(isInteger(fixture)).toBe(true));// ... or...const integerNumbers = [  [-1, true],  [-0, true],  [1, true]];test.each(integerNumbers)(    "isInteger passes for integer value %j with result %j",    (fixture, result) => expect(isInteger(fixture)).toBe(result));

Running Jest should now give us a report on which tests pass, and which tests fail, where every test will correspond to a fixture from our array of fixtures.

Note
%j is a printf formatting specifier that prints the value as JSON. It's a good choice for fixtures that contain values of different types.

How to group test cases in Jest into a test suite?

To group test cases in Jest into a test suite we can use the describe() function. It takes a suite name string and handler function as the first two arguments.

A test suite is a collection of test cases grouped together for execution purposes. The goal of a test suite is to organise tests by common behaviour or functionality. If all tests within a suite pass, we can assume that the behaviour or functionality meets expectations.

// isInteger.spec.jsconst isInteger = require("./isInteger");describe("isInteger", () => {    const integerNumbers = [-10, -1, 0, 1, 10];    test.each(integerNumbers)(        "passes for integer value %j",        (fixture) => expect(isInteger(fixture)).toBe(true)    );    const floatNumbers = [-10.1, -1.1, 0.1, 1.1, 10.1];    test.each(floatNumbers)(        "fails for non-integer value %j",        (fixture) => expect(isInteger(fixture)).toBe(false)    );});

Running Jest should now give us a report on which tests pass, and which tests fail, grouped into described test suites.

Note
describe() blocks can also be nested to create more complex test hierarchies.

How to run Jest every time files change?

To run Jest every time files change we can use the --watch and --watchAll flags.

The --watch flag will tell Jest to watch for changes in files tracked by Git. Jest will run only those tests affected by the changed files. For this to work, the project must also be a Git repository.

The --watchAll flag will tell Jest to watch all files for changes. Whenever a file changes, Jest will run all tests.

Both --watch and --watchAll modes support additional filtering of tests while the tests are running. This makes it possible to only run tests matching a file name, or only run failing tests.

# Runs tests on changed files only and re-runs for any new change# Note: the project must also be a git repositoryjest --watch# Runs tests on all files and re-runs for any new changejest --watchAll

How to get a test coverage report with Jest?

To get a test coverage report with Jest we can use the --coverage flag.

Test coverage is a software testing metric that describes how many lines of source code (statements) of the tested unit are executed (covered) by tests. A test coverage of 100% for a unit means every line of code in the unit has been called by the test.

We should always aim for a high test coverage - ideally 100% - but also keep in mind that total coverage does not mean we tested all cases, only lines of code.

# Runs tests and prints a test coverage afterwardsjest --coverage

Note
We can combine different flags to get more features out of Jest. For example, to watch all files and get a coverage report we can run jest --watchAll --coverage.

With that we're all set! We can now write tests and run them when whenever a file is changed, and also review test coverage reports for covered and uncovered lines of code.!

Jest unit test example code

To install Jest:

# For NPM usersnpm install --save-dev jest @types/jest# Yarn usersyarn add --dev jest @types/jest

The unit to be tested in isInteger.js:

// isInteger.jsmodule.exports = (value) => !isNaN(parseInt(value, 10));

The unit test in isInteger.spec.js:

// isInteger.spec.jsconst isInteger = require("./isInteger");describe("isInteger", () => {    const integerNumbers = [-10, -1, 0, 1, 10];    test.each(integerNumbers)(        "passes for integer value %j",        (fixture) => expect(isInteger(fixture)).toBe(true)    );    const floatNumbers = [-10.1, -1.1, 0.1, 1.1, 10.1];    test.each(floatNumbers)(        "fails for non-integer value %j",        (fixture) => expect(isInteger(fixture)).toBe(false)    );});

The test script in package.json:

jest --watchAll --coverage

Homework and next steps

  • Write more comprehensive tests. How are strings handled? Objects? null and undefined? Consider adding more fixtures to cover these cases.
  • Fix the code so the tests pass or write a newer, better implementation.
  • Achieve 100% code coverage in the coverage report.

Thank you for taking the time to read through this article!

Have you tried writing unit tests in Jest before? How do you feel about Jest?

Leave a comment and start a discussion!


Original Link: https://dev.to/dstrekelj/how-to-write-unit-tests-in-javascript-with-jest-2e83

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To