Mock dependency in jest with typescript

前端 未结 9 1046
梦谈多话
梦谈多话 2020-12-07 12:03

When testing a module that has a dependency in a different file. When assigning that module to be jest.Mock typescript gives an error that the method mock

相关标签:
9条回答
  • 2020-12-07 12:26

    You can use type casting and your test.ts should look like this:

    import * as dep from '../dependency';
    jest.mock('../dependency');
    
    const mockedDependency = <jest.Mock<typeof dep.default>>dep.default;
    
    it('should do what I need', () => {
      //this throws ts error
      // Property mockReturnValueOnce does not exist on type (name: string)....
      mockedDependency.mockReturnValueOnce('return');
    });
    

    TS transpiler is not aware that jest.mock('../dependency'); changes type of dep thus you have to use type casting. As imported dep is not a type definition you have to get its type with typeof dep.default.

    Here are some other useful patterns I've found during my work with Jest and TS

    When imported element is a class then you don't have to use typeof for example:

    import { SomeClass } from './SomeClass';
    
    jest.mock('./SomeClass');
    
    const mockedClass = <jest.Mock<SomeClass>>SomeClass;
    

    This solution is also useful when you have to mock some node native modules:

    import { existsSync } from 'fs';
    
    jest.mock('fs');
    
    const mockedExistsSync = <jest.Mock<typeof existsSync>>existsSync;
    

    In case you don't want to use jest automatic mock and prefer create manual one

    import TestedClass from './TestedClass';
    import TestedClassDependency from './TestedClassDependency';
    
    const testedClassDependencyMock = jest.fn<TestedClassDependency>(() => ({
      // implementation
    }));
    
    it('Should throw an error when calling playSomethingCool', () => {
      const testedClass = new TestedClass(testedClassDependencyMock());
    });
    

    testedClassDependencyMock() creates mocked object instance TestedClassDependency can be either class or type or interface

    0 讨论(0)
  • 2020-12-07 12:30

    I have found this in @types/jest:

    /**
      * Wrap a function with mock definitions
      *
      * @example
      *
      *  import { myFunction } from "./library";
      *  jest.mock("./library");
      *
      *  const mockMyFunction = myFunction as jest.MockedFunction<typeof myFunction>;
      *  expect(mockMyFunction.mock.calls[0][0]).toBe(42);
    */
    

    Note: When you do const mockMyFunction = myFunction and then something like mockFunction.mockReturnValue('foo'), you're a changing myFunction as well.

    Source: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/jest/index.d.ts#L1089

    0 讨论(0)
  • 2020-12-07 12:31

    I use the pattern from @types/jest/index.d.ts just above the type def for Mocked (line 515):

    import { Api } from "../api";
    jest.mock("../api");
    
    const myApi: jest.Mocked<Api> = new Api() as any;
    myApi.myApiMethod.mockImplementation(() => "test");
    
    0 讨论(0)
  • 2020-12-07 12:34

    A recent library solves this problem with a babel plugin: https://github.com/userlike/joke

    Example:

    import { mock, mockSome } from 'userlike/joke';
    
    const dep = mock(import('./dependency'));
    
    // You can partially mock a module too, completely typesafe!
    // thisIsAMock has mock related methods
    // thisIsReal does not have mock related methods
    const { thisIsAMock, thisIsReal } = mockSome(import('./dependency2'), () => ({ 
      thisIsAMock: jest.fn() 
    }));
    
    it('should do what I need', () => {
      dep.mockReturnValueOnce('return');
    }
    

    Be aware that dep and mockReturnValueOnce are fully type safe. On top, tsserver is aware that depencency was imported and was assigned to dep so all automatic refactorings that tsserver supports will work too.

    Note: I maintain the library.

    0 讨论(0)
  • 2020-12-07 12:36

    Cast as jest.Mock

    Simply casting the function to jest.Mock should do the trick:

    (dep.default as jest.Mock).mockReturnValueOnce('return')
    
    
    0 讨论(0)
  • 2020-12-07 12:39

    Use the mocked helper from ts-jest like explained here

    // foo.spec.ts
    import { mocked } from 'ts-jest/utils'
    import { foo } from './foo'
    jest.mock('./foo')
    
    // here the whole foo var is mocked deeply
    const mockedFoo = mocked(foo, true)
    
    test('deep', () => {
      // there will be no TS error here, and you'll have completion in modern IDEs
      mockedFoo.a.b.c.hello('me')
      // same here
      expect(mockedFoo.a.b.c.hello.mock.calls).toHaveLength(1)
    })
    
    test('direct', () => {
      foo.name()
      // here only foo.name is mocked (or its methods if it's an object)
      expect(mocked(foo.name).mock.calls).toHaveLength(1)
    })
    

    and if

    • you use tslint
    • ts-jest is in your dev-dependencies,

    add this rule to your tslint.json: "no-implicit-dependencies": [true, "dev"]

    0 讨论(0)
提交回复
热议问题