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.

        Python Class to Calculate Distance and Slope of a Line with Coordinates as Input

        🐍  Can be run on Jupyter Notebook #CLASS DESIGNED TO CREATE OBJECTS THAT TAKES COORDINATES AND CALCULATES DISTANCE AND SLOPE class Line:     def __init__(self,coor1,coor2):         self.coor1=coor1         self.coor2=coor2 #FUNCTION CALCULATES DISTANCE     def distance(self):         return ((self.coor2[0]-self.coor1[0])**2+(self.coor2[1]-self.coor1[1])**2)**0.5 #FUNCTION CALCULATES SLOPE         def slope(self):         return (self.coor2[1]-self.coor1[1])/(self.coor2[0]-self.coor1[0]) #DEFINING COORDINATES coordinate1 = (3,2) coordinate2 = (8,10) #CREATING OBJECT OF LINE CLASS li = Line(coordinate1,coordinate2) #CALLING DISTANCE FUNCTION li.distance() #CALLING SLOPE FUNCTION li.slope()