Turn your manual testers into automation experts! Request a DemoStart testRigor Free

Jest Testing: A Quick Guide to Effective Testing

Jest was created by Meta, mainly for testing React components. Over time, it emerged as the popular framework for testing React components, and now, many organizations are using Jest to test both front-end and back-end applications. Jest works with React apps, Babel, TypeScript, Node, Angular, and Vue-based projects. Jest got over 50 million downloads, marking it the most used framework for testing JavaScript apps.

Let’s start with unit testing first, and then we will look into further details about the framework.

Unit Testing

In unit testing, the individual component or unit of the application is tested in isolation. When we say component, it can be an individual function, method, or module. The unit test isolates that part of the code and validates its functionality. Unit testing can be considered the first testing conducted on the application. Developers usually perform it, but the QA team performs the same nowadays. The main idea of unit testing is to eliminate the bugs in small pieces of code itself. This helps to redefine the code and simplify the debugging. Unit testing can be performed manually or automated. Currently, automation is preferred as this becomes the first step in the continuous testing process.

Jest Framework

Usually, there is a misinterpretation that Jest is a library rather than a framework, which is wrong. Jest has a Command Line Interface (CLI) packed with many functionalities. Jest is built on top of Jasmine, a popular BDD tool. Jest can also be used to test every component of the application. Jest has an advantage over other tools because of its non-reliance on third-party apps. The configuration and set-up of Jest are convenient; thereby, it helps to reduce time consumption and complexity.

Now, let’s look at two main terminologies used in Jest. We can say four main features of Jest that stand out from other unit testing tools, they are:

  • Zero Config: Jest aims to work out of the box, config-free, on most JavaScript projects, Meaning Jest is easy to install as a project dependency, and with minimal config, we can start creating unit test scripts.
  • Snapshots: With this feature, we can avoid writing lengthy assertions for every unit; instead, we just create the snapshot of the assertions, and then every time we run, we need to validate against the snapshot only, thereby saving time.
  • Isolated: Here, Jest ensures that tests run in parallel, none influence others, so every test runs on its own process. Know more about parallel testing.
  • Great API: Jest has a rich API collection offering different assertion types that are well-documented and well-maintained.

Terminology in Jest

Two magical words are most commonly used in Jest: mock and spy. Let’s discuss more about each.

Mock

Mock functions are used to mock the actual implementation of a function. Consider the unit that we are going to test has a dependency on another unit, where there is an API call to that function, and based on the response, we may need to do some validations. So if that function is not ready or throws some error, our test case may give a false positive error. For such scenarios, we mock the API call so the mock API always returns an error-free value; thereby, we can validate if our function under test functions well.

In Jest, it’s effortless to create a mock. Let’s see an example.
const returnsTrue = jest.fn(() => false);
console.log(returnsTrue()); // false;

In the above example, jest. fn() creates the mock. Here, we are mocking a return value. So whenever the user calls returnsTrue, it gives the value false.

Spy

Spy is similar to mock, but the only difference is all the calls are tracked. This helps to track if the function is called the right number of times and if it works correctly. Let’s see a sample:
const greet = require('./greetings');
const jest = require('jest');

describe('greet function', () => {

  it('should call greet function with correct parameter', () => {

    // Spy on greet function
    const spy = jest.spyOn(greet, 'name');

    // Call the function with a test parameter
    greet('John');

    // Check if the spy was called with the correct parameter
    expect(spy).toHaveBeenCalledWith('John');

    // Restore the original implementation
    spy.mockRestore();

  });

});

Here, jest.spyOn() is used to track calls to the greet function and its arguments. The toHaveBeenCalledWith matcher checks if the function was called with the expected parameter. Finally, mockRestore() is used to restore the original function implementation after the test.

Jest Basics

Now, let’s look into a few basic terminologies we use while writing test scripts in Jest.

describe Blocks

They are used mainly to organize the test cases in logical groups. Using ‘describe‘, we can put a set of test cases under a single block. Consider that we have written test scripts for the login function. So we can group all the test cases of a specific method or class. Let’s see how it’s written:
describe('Test Suite for MyFunction', () => {

  it('should do something specific', () => {
    // Test something here
    expect(...).toBe(...);
  });

  it('should do something else, () => {
    // Another test
    expect(...).toEqual(...);
  });

  // More tests can be added here
});

So here we have grouped two validations under one single ‘describe’.

“It” or “Test” Tests

These keywords are used to start a new test case definition. The keyword ‘test’ is an alias for ‘it’. The most commonly used one is ‘it’. If you see the above example, we have started two test cases using the keywords ‘it’.

beforeAll and afterAll

If we need to make any prerequisite or to run any other function before starting the execution, we use the keyword ‘beforeAll’. So the action mentioned in ‘beforeAll’ will be executed before all test cases are managed. Similarly, if we need to perform some action, once all the test scripts are implemented, we call the method ‘afterAll’. Mostly, if we need to switch the state of a function to its ready state, we add that to ‘afterAll’ function.

Skip Tests

If we need to skip the test case that we have written because the function is not ready yet or we are facing some other issue, and we don’t want to run it, in that case, we can use the method ‘skip’. We can either skip the single test case or the whole block itself.
test.skip('should skip this test', () => {
  // Test implementation
});
Above, skip the individual test case alone.
describe.skip('Test Suite to skip', () => {
  it('test 1', () => {
    // Test 1 implementation
  });
  
  it('test 2', () => {
    // Test 2 implementation
  });
});

Here, we are skipping the whole block itself.

Matchers

Matchers are used for creating assertions combined with the ‘expect’ keyword. When we want to compare the test output with a value we expect the function to return, we use matchers. Let’s look into an example.
describe('multiply function', () => {
  it('multiplies two numbers correctly', () => {
    expect(multiply(3, 4)).toEqual(12);
    expect(multiply(-1, 5)).toEqual(-5);
    expect(multiply(0, 100)).toEqual(0);
  });
});

In this script:

  • We use ‘describe’ to group our tests for the multiply function.
  • Inside ‘describe’, we define individual test cases with it.
  • In each test case, we use ‘expect’ along with the ‘toEqual’ matcher to check if the result of the multiply function is as expected.

beforeEach and afterEach

These two keywords are used if we need to run any function before and after each test in the describe block. We can understand that with an example.
describe('add function', () => {

  // Runs before each test
  beforeEach(() => {
    console.log('Starting a test');
  });

  // Test cases
  it('adds two positive numbers', () => {
    expect(add(1, 2)).toBe(3);
  });

  it('adds two negative numbers', () => {
    expect(add(-1, -2)).toBe(-3);
  });

  // Runs after each test
  afterEach(() => {
    console.log('Finished a test');
  });
});

Here, ‘beforeEachruns before each test. It helps set up the environment for each test.afterEach runs after each test. It cleans up after each test, like resetting mock states.

Jest – Setup & Execution

Before installing Jest, we need to ensure we have JavaSDK installed, NodeJS and npm, and the correct versions of browser drivers are installed. Browser drivers are required to implement Selenium WebDriver’s protocol for converting the written commands to the browser’s native API. Also, Selenium WebDriver is required if we are testing the application in parallel on our local machine.

To install Jest, we need to pass the below command in the terminal:
npm install -g jest

Once Jest is installed, we can confirm its version by the command jest -version

We need to edit the package.json file to add Jest to our project. In the scripts section, add the following line:
"scripts": {
  "test": "jest"
}

This allows you to run tests using the npm test command.

Now, we can write our first script. Consider we have an add function in a file named mathFunctions.js. Let’s see the Jest test script for that function:
// mathFunctions.test.js
const { add } = require('./mathFunctions');

describe('mathFunctions', () => {

  // Test for add function
  describe('add function', () => {
    it('should return the sum of two numbers', () => {
      expect(add(1, 2)).toBe(3);
      expect(add(-1, 2)).toBe(1);
      expect(add(-1, -2)).toBe(-3);
    });

    it('should handle non-numeric inputs gracefully', () => {
      expect(add('a', 'b')).toBeNaN();
      expect(add(null, undefined)).toBeNaN();
      expect(add({}, [])).toBeNaN();
    });
  });

  // Additional tests for other functions can be added here
});

In this test script:

  • We import the add function from mathFunctions.js.
  • We use describe to create a test suite for mathFunctions.
  • Inside the describe block, we write individual test cases using it.
  • We use expect and matchers like toBe and toBeNaN to assert the expected outcomes.

To run the test, simply execute the npm test command in your terminal. Jest will automatically find and run files with .test.js or .spec.js suffixes.

Jest Reports

Jest provides the execution report in the command line itself; Jest also provides a test report in HTML format. For that, we need to install an additional package – jest-html-reporter. Once this is installed, we need to create a new package.json file with a different name but at the exact location of the package.json file. We can name it “jesthtmlreporter.config.json”. We need to add the below details to the file.
{
  "pageTitle": "HTML Report",
  "outputPath": "testReport.html",
  "includeFailureMsg": true
}

Also, in the scripts section in default package.json, we need to add the set CI=true.

Jest – Advantages and Limitations

Advantages Limitations
Zero Configuration: Jest works out of the box with minimal configuration, making it easy to set up. Performance: While fast in most cases, Jest can be slower than other testing frameworks, especially in large projects.
Snapshot Testing: Supports snapshot testing, which helps track changes in UI over time. Learning Curve: The extensive range of features and matches can overwhelm beginners.
Integrated Coverage Reports: Jest provides built-in support for test coverage reports. Heavier Framework: It’s a more extensive package compared to more minimalistic testing libraries.
Mocking and Spying: Easy mocking, spying, and stubbing capabilities. Integration with Non-React Projects: While it’s excellent for React, integration with other libraries/frameworks might require additional setup.
Watch Mode: Automatically runs tests related to changed files, making development more efficient. Isolation: Jest runs tests in parallel in isolated environments, which can sometimes lead to unexpected issues when not properly managed.
Great for React and JavaScript/TypeScript: Jest is optimized for testing React applications and supports JS/TS. Verbose Mocking: Setting up complex mocks can sometimes be lengthy and convoluted.
Rich APIs and Matchers: A wide range of APIs and matchers for various testing needs. Different Assertion Syntax: Those familiar with other testing frameworks might find Jest’s assertion syntax slightly different.
Community and Ecosystem: Strong community support and a rich ecosystem of plugins and extensions. DOM Testing Limitations: Jest is not a browser testing tool; it simulates the DOM in Node.js, which might only be accurate for some use cases.

Conclusion

Unit testing is the essential and critical testing for the application. Here, we are capturing the bug at its very early stage. For an application, we have a saying – The bug gets costly when it’s identified at a later stage. Here are the risks and impacts of late bug detection.

So, catching bugs at unit testing is always excellent and cost-effective. Jest provides a very nice interface for unit testing. Though it supports JavaScript and React applications, it still brings many limitations to the software development process.

Join the next wave of functional testing now.
A testRigor specialist will walk you through our platform with a custom demo.
Related Articles

Top 5 QA Tools to Look Out For in 2024

Earlier, test scripts were written from a developer’s perspective to check whether different components worked correctly ...

Best Practices for Creating an Issue Ticket

“Reminds me of the awesome bug report I saw once: Everything is broken. Steps to reproduce: do anything. Expected result: it ...