Testing with Jest

27 Jan, 2022
Jest is a Javascript Testing Framework by Facebook. It is used most commonly for unit testing. Unit testing is when you provide input to a unit of code(usually, a function) and match the output with the expected output. 
Jest Features:  
zero config: As we will see later in this article, close to none configuration is required to get started with writing tests and deploying them. However, a config file can be supplied to the test suite as well.
snapshots: Jest has the ability to enable snapshot testing as well. Essentially, the snapshots are matched with the saved snapshot and check for matching functionality.
isolated tests: Jest tests are run parallelly to improve run time.
Setting up a jest project  
Install Jest using npm:  
npm install jest. The -D keyword installs jest as dev dependency
Project Structure: In the project root directory, make a tests folder. This folder will store all the test files. Note that the js files(which are to be tested) are mapped by their names. For example, index.js is mapped to index.test.js. This index.test.js file is placed in the ‘tests’ folder. This is the conventional project structure.
Start testing:  
To start, lets see a basic test workflow.  
test – adding two pos nums
To run the test, use the script  
npm run test
This will look for the test script mentioned in the package.json of the project.  
pkg-json-test-script-ss. See test in scripts
We will use the ‘expect’ method to test our functions. The functions can also be tested using ‘describe’ and ‘it’. 
A basic test: adding two positive nums and checking result.  
//index.js
testForAdd: (a, b) => { return (a + b) },

//index.test.js
test('test adding two positive nums', function() {
    expect(testFns.testForAdd(4, 5)).toBe(9);
});
When the ‘npm run test’ is run, the index.test.js file is gone through. Then the testForAdd function is run which is placed in the ‘testFns’ object. The toBe is used to ‘match’ the returned response from the test to what is expected. This ‘result matching’ leads to either a ‘fail’ or a ‘pass’. 
 
test adding two pos nums
The following test will fail because of the ‘toBe(8)’.  
//example of a failing test
 test('test adding two positive nums - failing test', function() {
     expect(testFns.testForAdd(4, 5)).toBe(8);
 });
Opposite of toBe: not.toBe() The opposite of the ‘toBe’ matcher is created by simply prefixing it with ‘not.’ For example:  
//test successful - test for opposite of a matcher.
//The following test will 'Pass' if the returned value is not equal to 8.
test('test adding two positive nums - failing test', function() {
    expect(testFns.testForAdd(4, 5)).not.toBe(8);
});
Using ‘toBe’ with JS Objects: Let’s think of a case where every field of a JS object is to be tested. Jest provides us a way to do this using ‘toEqual’. The ‘toEqual’ is a deep-matcher(checks every field and sub-fields possible).  
//expect toEqual example - check every field's value
// testFns.test_toEqual(gfgObj)
test('check gfgObj toEqual', () => {
    let gfgObj = { name: "GFG" };
    gfgObj['type'] = "company";
    expect(gfgObj).toEqual({ name: "GFG", type: "company" });
});
Running the above test will ‘Pass’.  
gfgobj toEqual
Another variation of doing this is matching two objects using the ‘toEqual’. This is done so like this :  
test('check gfgObj toEqual', () => {
    let gfgObj = {
        name: "GFG",
        type: "company",
        members: {
            employees: 100,
            contributors: 500
        }
    };

    let testObj = {
        name: "GFG",
        type: "company",
        members: {
            employees: 100,
            contributors: 500
        }
    };
    expect(gfgObj).toEqual(testObj);
});
This test demonstrates the deep-matching feature of toEqual. The above test passes as every key-pair in gfgObj matches with testObj.  
gfgobj-testobj-toequal
toBeCloseTo – for floating point numbers and other approximate matches  
//see here that a+b != c even though simple addition is taking place.
> var a = 1.32
> undefined
> var b = 2.31
> undefined
> c = a+b;
> 3.63
> var res = false;
> if(c==(a+b)) {c=true;}
> undefined
> c
> false
In such conditions, it is good to use ‘toBeCloseTo’ matcher from the Jest library.  
test('floating point number - use toBeCloseTo instead', function() {
    // const num1 = 0.3;
    // const num2 = 0.2;
    const result = 9.31 + 9.2;

    expect(result).toBeCloseTo(18.51);
})
The above test passes too. 
matching truthy and falsy: https://jestjs.io/docs/en/using-matchers#truthiness A lot of times, tests are written to check for truthy and falsy values returned by ‘expect’. Falsy values in JS. Truthy values in JS.  
//checking for truthy values - All the tests will return truthy.
test('check for truthy', function() {
    const gfgObj = {
        first: null,
        second: undefined,
        third: false
    }
    expect(gfgObj.first).not.toBeTruthy(); // True - Pass
    expect(gfgObj.second).toBeUndefined(); // True - Pass
    expect(gfgObj.third).toBeFalsy();      // True - Pass
})
The above test passes.  
truthy-test-js-all-test-passing
However, if any of the ‘expect’ above fails, Jest returns meaningful error messages like below. 
Note that in above case, if any of the ‘expect’-s fail, the test also completely fails.  
truthy-test-js-one-expect-failing-so-test-failing
Matching Numbers:  
//tests for Number matches
test('test for numbers', function() {
    const result = 3 + 9;
    // expect(result).toBe(12); //the plain old matcher
    expect(result).not.toBeLessThan(10); // result > 10
    expect(result).toBeLessThan(15);  // result < 15
    expect(result).not.toBeGreaterThan(15); // result  10
    expect(result).toBeGreaterThanOrEqual(12);  //result >= 12
    // expect(result).not.toBeGreaterThanOrEqual(12); // result == 12, this Fails
    // expect(result).toBeLessThanOrEqual(10); // result >= 10, this Fails
})
 
number matching using jest
Testing values contained in arrays: We can also test if particular values are contained in an array. Note that this test will ‘Fail’ if at least one value is not present in the array. For example,  
//testing arrays
const gfgUsers = [
    'user1',
    'user2',
    'user3'
];
test('test for a value in gfgUsers', function() {
    // expect(gfgUsers).toContain('user2');
    // expect(gfgUsers).not.toContain('user2');
    //expect array containing
     expect(gfgUsers).toEqual(expect.arrayContaining(['user1', 'user3']));
})
Th above test passes as user1 and user3 are present in gfgUsers.  
user1-and-user3-in-gfgUsers
However, the following test will fail because ‘user4’ is not present in gfgUsers.  
//testing arrays
const gfgUsers = [
    'user1',
    'user2',
    'user3'
];
test('test for a value in gfgUsers', function() {
    //expect array containing
     expect(gfgUsers).toEqual(expect.arrayContaining(['user1', 'user4']));
})
 
arraycontaining-fail-due-to-user4-absence
Testing using Regex:  
test('string match tests - toMatch - used for regex-es', function() {
    const str = 'GeeksforGeeks';
    // expect(str).toMatch(/f/);
    // expect(str).toMatch(/Z/);
    //you can create more complex Regexes
    const str1 = 'This is a test paragraph. I wrote it.'
    expect(str1).toMatch(/[pP][hH][\.]/);  //matches 'ph.' in the word 'paragraph'
})
 
tomatch-matching-ph-in-paragraph
Extending the Matchers Jest also has the provision to extend its ‘Matchers’ functionality, which is accomplished using the ‘expect.extend()’ keyword. The .extend() function is passed matchers as objects. Syntax: expect.extend({matcher1, matcher2}) ; For example, if we want to build a matcher that checks for a phrase presence in a string:  
expect.extend({
stringPresent(receivedString, phraseString) {
bool phrasePresent = true;
var re = new RegExp(phraseString);
if (re.test(receivedString)) {
    phrasePresent = true;
} else {
    phrasePresent = false;
}
if (phrasePresent === true) {
      return {
        message: () =>
          `string present`,
        pass: true,
      };
    } else {
      return {
        message: () =>
          `string absent`,
        pass: false,
      };
    }
},
});
Dealing with exceptions We can also check the types of errors that a unit of code throws. We can check the error thrown by name, message, object, etc. 
Syntax:expect( fnName() ).toThrow( error ); The error parameter/argument is optional here. 
Let’s suppose that we want to test a function by the message of the error thrown.  
function testGFGMessage() {
  throw new Error('this is testGFGMessage function error');
}
test('testing testGFGMessage', function(){
  expect(testGFGMessage).toThrow('this is testGFGMessage function error');
})
There are many other ways to throw Errors and check for them. A detailed reference can be found here. 
Skipping/Running a subset of tests https://jestjs.io/docs/en/api#testskipname-fn Jest also has a provision for skipping specific tests while running the test suite. To implement it, simply use the ‘skip’ keyword. For example,  
function addFn(num1, num2){
  return num1 + num2;
}

test.skip('skip test example - skipping the addition test', () => {
  expect(addFn(2, 3)).toBe(5);
});
The opposite of this is implementing only a subset of tests, which is achieved by using the ‘only’ keyword. For example,  
function addFn(num1, num2){
  return num1 + num2;
}

test.only('skip test example - skipping the addition test', () => {
  expect(addFn(2, 3)).toBe(5);
});
 
saurabh1990aror
Technical Scripter 2019
Technical Scripter
Web Technologies
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Top 10 Projects For Beginners To Practice HTML and CSS Skills
Installation of Node.js on Linux
Difference between var, let and const keywords in JavaScript
How to insert spaces/tabs in text using HTML/CSS?
How to fetch data from an API in ReactJS ?
Differences between Functional Components and Class Components in React
Remove elements from a JavaScript Array
REST API (Introduction)
Node.js fs.readFileSync() Method
How to set the default value for an HTML <select> element ?