'How to strongly type jest mocks

I would like to strongly type my jest mocks. To a certain extent, I can make it work but when a class has private properties I'm stuck.

An additional question, when I use mocks (the way I currently do) the return type is of the original type but when I have to access any method added by Jest I have to typecast it so jest.Mock to access a method. Is there a better way to do this? I've tried to work with jest.Mock, jest.Mocked, jest.MockInstance.

If anyone could point me in the right direction that would be great!

class MyTest {
    constructor(private readonly msg: string) {}

    public foo(): string {
        return this.msg;
    }
}

const myTestMock: jest.Mock<MyTest, [string]> = jest.fn<MyTest, [string]>(() => ({
    msg: 'private',
    foo: jest.fn().mockReturnValue('aaa'),
}));
// Results in error:
// Type '{ msg: string; foo: Mock<any, any>; }' is not assignable to type 'MyTest'.
// Property 'msg' is private in type 'MyTest' but not in type '{ msg: string; foo: Mock<any, any>; }'

const myTestMockInstance: MyTest = new myTestMock('a');
console.log(myTestMockInstance.foo()); // --> aaa

// Accessing jest mock methods:
(<jest.Mock>myTestMockInstance).mockClear(); // <-- can this be done without type casting

Dirty workaround:

const myTestMock: jest.Mock<MyTest, [string]> = jest.fn<MyTest, [string]>(
    // Cast to any to satisfy TS
    (): any => ({
        msg: 'private',
        foo: jest.fn().mockReturnValue('aaa'),
    })
);


Solution 1:[1]

There is a library that can help you with strongly typed mocks in Typescript with Jest: jest-mock-extended

I'm not sure you should be accessing private properties of your mock. As Kim Kern says, you should only be interested in the public interface of the dependency of the unit under test.

jest-mock-extended contains a mock() method which returns a MockProxy which allows you to access .mockReturnValue() etc.

import { mock } from "jest-mock-extended";

interface WidgetService {
  listMyWidgets(): { id: string }[];
};

const mockedService = mock<WidgetService>();
mockedService.listMyWidgets.mockReturnValue([{ id: 'widget-1' }]);

Solution 2:[2]

  1. There is a helper which makes types: look ts-jest
  1. usually you should test public interface, not an implementation. Instead of testing private methods you could extract that logic to a public interface (maybe to another class that provides the interface)

Solution 3:[3]

You can use jest.fn() to mock instantiating the class object and omit specifying the type on the variable and let Typescript infer it.

myTestMockInstance will not have the type of MyTest because you are mocking it.

class MyTest {
   constructor(private readonly msg: string) {}

   public foo(): string {
      return this.msg
   }
}

const myTestMock = jest.fn((msg: string) => ({
   msg,
   foo() {
      return this.msg
   }
}))

const myTestMockInstance = new myTestMock('a')
console.log(myTestMockInstance.foo()) // --> a

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 jjt
Solution 2
Solution 3 Toomuchrice4u