Skip to main content

Unit testing in NodeJS with Jest


  • Install Jest
  • Run npm install jest --save-dev
  • In package.json set in directories "test": "tests" and in scripts "test": "jest"
  • Run npm test in terminal
  • Add a test folder to the project
  • Inside test folder add file named after the file name that has functions that you want to test
  • Inside this file write your test
  • test('Test Name', () => {});
  • Run npm test
  • Change test file
test('Test Name', () => {
  throw new Error('Something Failed');
});
  • Suppose we have a file named lib.js with following code
module.exports.absolute = function(num) {
  if(num >= 0 ) return num;
  return -num;
}
  • Now we test it
  • Create a test file in test folder named lib.test.js
const lib = require('../lib');

test('absolute - should return a positive number if input is positve number', () => {
  expect(lib.absolute(1)).toBe(1);
});

test('absolute - should return a positive number if input is zero', () => {
  expect(lib.absolute(0)).toBe(0);
});

test('absolute - should return a positive number if input is negative number', () => {
  expect(lib.absolute(-1)).toBe(1);
});
  • Run npm test
  • All three passes
  • Let's group the above three tests using describe
const lib = require('../lib');

describe('absolute', () => {
  it('should return a positive number if input is positve number', () => {
    expect(lib.absolute(1)).toBe(1);
  });

  it('should return a positive number if input is zero', () => {
    expect(lib.absolute(0)).toBe(0);
  });

  it('should return a positive number if input is negative number', () => {
    expect(lib.absolute(-1)).toBe(1);
  });
});
  • it can be used instead of test
  • Let us now try an see similar testing for strings
  • Suppose we have a function for string manipulation as follows:
module.exports.greet = function(name) {
  return `Hello ${name}!`;
}
  • Now we write a test for it in our test file
const lib = require('../lib');

describe('greet', () => {
  it('should return a greeting Hello <name>!', () => {
    expect(lib.greet("Vinit")).toBe("Hello Vinit!");
  });

  it('should return a greeting with the name', () => {
    expect(lib.greet("Vinit")).toMatch(/Vinit/);
  });

  it('should return a string that contains the name', () => {
    expect(lib.greet("Vinit")).toContain("Vinit");
  });
});
  • Running this test will check the result in three different ways
    • First checks if result is exactly the same
    • Second and third checks if result contains the name.
    • Second does that with Regex
  • Now let us test arrays
  • Let's create a function that returns an array
const lib = require('../lib');

describe('getCurrencies', () => {
  it('too general', () => {
    expect(lib.getCurrencies()).toBeDefined();
    expect(lib.getCurrencies()).not.toBeNull();
  });

  it('too specific', () => {
    const result = lib.getCurrencies();
    expect(result[0]).toBe('USD');
    expect(result[0]).toBe('INR');
    expect(result[0]).toBe('EUR');
    expect(result.length).toBe(3);
  });

  it('should return an array of currency codes containing USD, INR, EUR', () => {
    const result = lib.getCurrencies();
    expect(result).toContain('USD');
    expect(result).toContain('INR');
    expect(result).toContain('EUR');
  });

  it('should return an array of currency codes containing USD, INR, EUR', () => {
    const result = lib.getCurrencies();
    expect(result).toEqual(expect.arrayContaining(['EUR','INR','USD']));
  });
});

  • Testing Objects

const lib = require('../lib');

describe('getProducts', () => {
  it('should return the product with given id', () => {
    // too specific
    expect(lib.getProduct(1)).toEqual({ id: 1, price: 10 });
    expect(lib.getProduct(1)).toMatchObject({ id: 1, price: 10 });
    expect(lib.getProduct(1)).toHaveProperty({ id: 1 });
  });
});
  • Note: We use toEqual here and not toBe, because toBe matches the location, which are different for both, while toEqual matches the values at the locations, which are same
  • toMatchObject is used when I am not sure if the key value pairs mentioned are all that are. There could be more.
  • toHaveProperty only checks if the object exists
TESTING ERRORS

  • Testing if a function throws error where required

describe('registerUser', () => {
  it('should throw error if username is falsy', () => {
    // null, undefined, NaN, "", 0, or false
    const args = [null, undefined, NaN, "", 0, false];
    args.forEach(a => {
      expect(() => { lib.registerUser(a) }).toThrow();
    });
  });

  it('should return a user object if valid username is passed', () => {
    const result = lib.registerUser('Vinit');
    expect(result).toMatchObject({ username: 'Vinit' });
    expect(result.id).toBeGreaterThan(0);
  });
});

        Comments

        Popular posts from this blog

        Python - List - Append, Count, Extend, Index, Insert, Pop, Remove, Reverse, Sort

        🐍 Advance List List is widely used and it's functionalities are heavily useful. Append Adds one element at the end of the list. Syntax list1.append(value) Input l1 = [1, 2, 3] l1.append(4) l1 Output [1, 2, 3, 4] append can be used to add any datatype in a list. It can even add list inside list. Caution: Append does not return anything. It just appends the list. Count .count(value) counts the number of occurrences of an element in the list. Syntax list1.count(value) Input l1 = [1, 2, 3, 4, 3] l1.count(3) Output 2 It returns 0 if the value is not found in the list. Extend .count(value) counts the number of occurrences of an element in the list. Syntax list1.extend(list) Input l1 = [1, 2, 3] l1.extend([4, 5]) Output [1, 2, 3, 4, 5] If we use append, entire list will be added to the first list like one element. Extend, i nstead of considering a list as one element, it joins the two lists one after other. Append works in the following way. Input l1 = [1, 2, 3] l1.append([4, 5]) Output...

        Difference between .exec() and .execPopulate() in Mongoose?

        Here I answer what is the difference between .exec() and .execPopulate() in Mongoose? .exec() is used with a query while .execPopulate() is used with a document Syntax for .exec() is as follows: Model.query() . populate ( 'field' ) . exec () // returns promise . then ( function ( document ) { console . log ( document ); }); Syntax for .execPopulate() is as follows: fetchedDocument . populate ( 'field' ) . execPopulate () // returns promise . then ( function ( document ) { console . log ( document ); }); When working with individual document use .execPopulate(), for model query use .exec(). Both returns a promise. One can do without .exec() or .execPopulate() but then has to pass a callback in populate.

        683 K Empty Slots

          Approach #1: Insert Into Sorted Structure [Accepted] Intuition Let's add flowers in the order they bloom. When each flower blooms, we check it's neighbors to see if they can satisfy the condition with the current flower. Algorithm We'll maintain  active , a sorted data structure containing every flower that has currently bloomed. When we add a flower to  active , we should check it's lower and higher neighbors. If some neighbor satisfies the condition, we know the condition occurred first on this day. Complexity Analysis Time Complexity (Java):  O(N \log N) O ( N lo g N ) , where  N N  is the length of  flowers . Every insertion and search is  O(\log N) O ( lo g N ) . Time Complexity (Python):  O(N^2) O ( N 2 ) . As above, except  list.insert  is  O(N) O ( N ) . Space Complexity:  O(N) O ( N ) , the size of  active . Approach #2: Min Queue [Accepted] Intuition For each contiguous block ("window") of  k  po...