Jest

Author: Muhammad AbdulMoiz

Delightful JavaScript Testing

easy to configure

meaningful error messages

parallel tests

built in mocking

async tests

smart watchmode

compile to js friendly

familiar syntax

Installing Jest

// Install Jest using npm
npm install --save-dev jest

// Installing via yarn
yarn add --dev jest

package.json

{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch"
  }
}

Default test files consideration

                        __tests__
            
                            *.spec.js
                            *.js
                        package.json
                        *.test.js
                        *.spec.js

Over ride default regex via package.json

{
  "jest": {
    "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(tsx?|jsx?)$"
  }
}

Familiar Syntax

Jest uses jasmine as a test runner but it does so much around it

                /*Global Methods*/
                
                - afterAll(fn)
                - afterEach(fn)
                - beforeAll(fn)
                - beforeEach(fn)
                - describe(name, fn)
                - describe.only(name, fn)
                - describe.skip(name, fn)
                - require.requireActual(moduleName)
                - require.requireMock(moduleName)
                - test(name, fn)
                - test.only(name, fn)
                - test.skip(name, fn)

How Jest test suite looks like?

        
describe('My Test suite', () => {

    beforeAll(fn);
  
    beforeEach(fn);
    
    test('first test', fn);
    
    test('second test', fn);
  
    .
    .
    .
  
    afterEach(fn);
    
    afterAll(fn)
  
});

Lets do some code now!

        test('two plus two is four', () => {
          expect(2 + 2).toBe(4);
        });
        
        test('object assignment', () => {
          const data = {one: 1};
          data['two'] = 2;
          expect(data).toEqual({one: 1, two: 2});
        });
        
        test('adding positive numbers is not zero', () => {
          for (let a = 1; a < 10; a++) {
            for (let b = 1; b < 10; b++) {
              expect(a + b).not.toBe(0);
            }
          }
        });

Expect

For complete list of matchers which jest provides visit following link

When you're writing tests, you often need to check that values meet certain conditions. expect gives you access to a number of "matchers" that let you validate different things.

            expect.anything()
            expect.any(constructor)
            expect.arrayContaining(array)
            expect.objectContaining(object)
            expect.stringContaining(string)
            expect.stringMatching(regexp)
            .not
            .resolves
            .rejects
            .toBe(value)
            .toHaveBeenCalled()

You can use expect.extend to add your own matchers to Jest.

expect.extend({
  toBeDivisibleBy(received, argument) {
    const pass = (received % argument == 0);
    if (pass) {
      return {
        message: () => (
          `expected ${received} not to be divisible by ${argument}`
        ),
        pass: true,
      };
    } else {
      return {
        message: () => (`expected ${received} to be divisible by ${argument}`),
        pass: false,
      };
    }
  },
});

test('even and odd numbers', () => {
  expect(100).toBeDivisibleBy(2);
  expect(101).not.toBeDivisibleBy(2);
});

Asynchronous testing

            test('async test', (done) => {
            someFn(function(err){
                if(err){
                    return done.fail('Error message!');
                }
                expect()
                done();
            })
            });
            
            test('async test', (done) => {
                return somePromise()
                .then(() => {
                    //testing promise resolve
                    expect();
                })
                .catch(() => {
                    // testing rejections
                    expect();
                })
            })

While running asynchronous test either you need to return a promise or use jasmine done argument

Asynchronous testing

//Wrong way of testing asynchronous method
//It won't fail OR run into any error because expect is never run  
           
           test('async test', () => {
                someFn(function(err){
                   expect();
                })
           });
            

If you are not using done for testing in callbacks, its not testing correctly

Asynchronous testing

//Wrong way of testing asynchronous method
//It won't fail OR run into any error because expect is never run  
           
           test('async test', () => {
                someFn(function(err){
                   expect();
                })
           });
            

If you are not using done for testing in callbacks, its not testing correctly

Parallel test performance

Running suites in parallel will definitely increase speed which decreases running time

Mocking with Jest

Mock functions make it easy to test the links between code by erasing the actual implementation of a function

     const someMockFn = jest.fn();

     // The function was called exactly once
     expect(someMockFunction.mock.calls.length).toBe(1);

     // The first arg of the first call to the function was 'first arg'
     expect(someMockFunction.mock.calls[0][0]).toBe('first arg');

     // The second arg of the first call to the function was 'second arg'
     expect(someMockFunction.mock.calls[0][1]).toBe('second arg');

     // This function was instantiated exactly twice
     expect(someMockFunction.mock.instances.length).toBe(2);

     // The object returned by the first instantiation of this function
     // had a `name` property whose value was set to 'test'
     expect(someMockFunction.mock.instances[0].name).toEqual('test');

Mocking Functions

     const myMock = jest.fn();
     console.log(myMock());
     // > undefined

     myMock.mockReturnValueOnce(10)
     .mockReturnValueOnce('x')
     .mockReturnValue(true);

     console.log(myMock(), myMock(), myMock(), myMock());
     // > 10, 'x', true, true

     const myMockFn = jest.fn(cb => cb(null, true));

     myMockFn((err, val) => console.log(val));
     // > true

     myMockFn((err, val) => console.log(val));
     // > true

Mocking Modules

     // foo.js
     module.exports = function() {
     // some implementation;
     };

     // test.js
     jest.mock('../foo'); // this happens automatically with automocking
     const foo = require('../foo');

     // foo is a mock function
     foo.mockImplementation(() => 42);
     foo();
     // > 42

Snapshot Testing

Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly.

Instead of rendering the graphical UI, which would require building the entire app, you can use a test renderer to quickly generate a serializable value for your DOM tree

Snapshot Testing

As we haven't look into the DOM testing with test, consider following example

expect(someFn()).toMatchSnapshot();// It will automatically create snapshot files

//In "ROOT/__snapshots__" folder 

//Snap shot file contain following data w.r.t each test

exports[`should test ....`] = `<some output value>`; 


//If test is failed due to old snap shot issue we need to update snapshots

//command for updating snapshots
jest --updateSnapshot

Using Jest with Typescript

//Installing required dependencies
npm install --save-dev ts-jest @types/jest

//Modify packageJSON

{
  "jest": {
    "transform": {
      "^.+\\.tsx?$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
    },
    "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(tsx?|jsx?)$",
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js",
      "json",
      "jsx"
    ]
  }
}

Can we use Jest with Angular?

Jest is built by facebook primarily for testing React framework but JEST team has done a great job and it can be use with any Javascript framework

Following is an article for discussion of Angular 2 testing with JEST

Feature request in angular cli

Vue.js with JESt

 Migrating to Jest

//Installing jest-codemods globally
npm install -g jest-codemods

//At root of your app run following command
jest-codemods

If you are using Mocha, AVA, chai or Tape, you can use the third-party jest-codemods to do most of the dirty migration work. It runs a code transformation on your codebase using jscodeshift.

 Migrating to Jest

It will ask for the frame work to migrate and run migration scripts as shown below

Thats it from the session!!

Thanks for watching and listening!!
Is there any questions?

JEST

By Muhammad AbdulMoiz

JEST

Javascript testing with JEST

  • 1,103