My learning diary

Jest for the First Time

I needed to test a feature which I’ve been building for GovTech’s FormSG. To test it, I had to mock a function in the superclass of an instance generated by a factory method call. I once considered stripping away the barriers between the mock and the test. But, I didn’t want to refactor a codebase which I’m not too familiar with.

Below is a visualisation of the “barriers” between the mock and the test:

  • Test calls a method. I couldn’t understand why the method is not in the test folder. Update: It seems that the actual code in src/main is using this method too.
  • Method calls a factory method which creates an instance whose method is what I want to mock.
  • The superclass of the instance defines the method which I want to mock.

At first, I made the mistake of calling jest.mock(...) on the superclass. When I mocked the child class and overrode the target method in the child class with the mock, the tests passed.

// Suppose I'm mocking someMethod() of Blah in Blah.js:
// In the folder containing Blah.js, create __mocks__/Blah.js:
class Blah extends BlahParent {
 // Paste original code of Blah here

 someMethod() {
 // Mock implementation here
 }
}

// Test file
jest.mock('path to Blah.js');

// Everything else remains the same

I was critical of this approach because I had to copy code from the real child class. What if the implementation of the real child class changes? The mock wouldn’t get updated.

My buddy recommended the following changes to the test file:

import Blah from './Blah';
import { mocked } from 'ts-jest/utils';

jest.mock('./Blah')
const MockBlah = mocked(Blah, true);
MockBlah.someMethod.mockReturnValue('Whatever I want.');

I got the following error: Property 'someMethod' does not exist on type 'MockedObjectDeep<typeof Blah>'. After some googling, I got reminded of the prototype field in JavaScript objects. I guess I should be using MockBlah.prototype.someMethod instead.

In contrast, Stack Overflow users recommended using spyOn and mockImplementation to mock a function.

jest.spyOn(Blah.prototype, 'someMethod').mockImplementation(() => 'Whatever I want.');
// Test code here

I also read the Jest docs to confirm my findings. In contrast, I didn’t verify the cause of the Property does not exist on type error. Would you do the honours…?