Search code examples
typescriptunit-testingjestjsts-jest

How to use Jest to monitor ES6 method calls?


Testing a class that calls a utilities class, with typescript. The goal is to mock the utilities class and test the data passed to the calls.

A class to test

import {Utils} from "~/src/play/utils";

export class Actions {
  constructor() {
}

async readFile(): Promise<void> {
   const utils = new Utils();
   await utils.action(5)
 }
}

const actions = new Actions();
actions.readFile()

Utilities class

export class Utils {
  constructor() {}

  async action(count: number): Promise<boolean> {
    console.log(count);
    return Promise.resolve(true);
  }
}

Test script

// @ts-ignore
import {Utils} from "./utils";
import {Actions} from "~/src/play/actions";

const mockUtils = jest.fn();
jest.mock('../utils', () => {
  return {
    Utils: jest.fn().mockImplementation(() => {
        return {
            default: () => {return {utils: mockUtils}},
            action: () => {
                return 5;
            }
        }
    })
   }
})

describe('actions', () => {
  beforeEach(() => {
    //  Utils.mockClear();
    mockUtils.mockClear();
  })

  test('success', async () => {
    const actions = new Actions()
    await actions.readFile()
    expect(mockUtils).toHaveBeenCalledWith(2)
  })
 })

It doesn't appear to be picking up the mock. The result is

Error: expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: 2

Number of calls: 0

at Object.<anonymous> (/Users/jrobens/NetBeansProjects/azuron/winpay-uploader/src/play/__test__/actions.spec.ts:28:27)
at processTicksAndRejections (internal/process/task_queues.js:95:5)

Taking @Stanislav Šolc advice add the return and argument value, including async

// @ts-ignore
import {Utils} from "../utils";
import {Actions} from "../actions";

const mockUtils = jest.fn();
jest.mock('../utils', () => {
return {
    Utils: jest.fn().mockImplementation(() => {
        return {
            default: () => {return {utils: mockUtils}},
             action: (count: number): Promise<boolean> => {
                 console.log(count);
                 return Promise.resolve(true);
            }
        }
    })
}
})

The result doesn't change


Solution

  • You can use jest.spyOn(object, methodName) to mock Utils.prototype.action() method. Use jest.restoreAllMocks() restores all mocks back to their original value in afterEach hook. Prevent the value of mock from affecting other test cases.

    E.g.

    actions.ts:

    import { Utils } from './utils';
    
    export class Actions {
      constructor() {}
    
      async readFile(): Promise<void> {
        const utils = new Utils();
        await utils.action(5);
      }
    }
    

    utils.ts:

    export class Utils {
      constructor() {}
    
      async action(count: number): Promise<boolean> {
        console.log(count);
        return Promise.resolve(true);
      }
    }
    

    actions.test.ts:

    import { Utils } from './utils';
    import { Actions } from './actions';
    
    describe('actions', () => {
      afterEach(() => {
        jest.restoreAllMocks();
      });
    
      test('success', async () => {
        const actionSpy = jest.spyOn(Utils.prototype, 'action').mockResolvedValueOnce(false);
        const actions = new Actions();
        await actions.readFile();
        expect(actionSpy).toHaveBeenCalledWith(5);
      });
    });
    

    test result:

     PASS  examples/68879782/actions.test.ts (10.186 s)
      actions
        ✓ success (3 ms)
    
    ------------|---------|----------|---------|---------|-------------------
    File        | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ------------|---------|----------|---------|---------|-------------------
    All files   |   71.43 |      100 |      75 |   71.43 |                   
     actions.ts |     100 |      100 |     100 |     100 |                   
     utils.ts   |   33.33 |      100 |      50 |   33.33 | 5-6               
    ------------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        10.827 s, estimated 11 s