typescript로 jest의 모의 종속성


94

다른 파일에서 종속성이있는 모듈을 테스트 할 때. 해당 모듈을 jest.Mocktypescript로 할당 하면 메서드 mockReturnThisOnce(또는 다른 jest.Mock 메서드)가 종속성에 존재하지 않는다는 오류가 발생하는 경우 이는 이전에 입력 되었기 때문입니다. jest.Mock에서 유형을 상속하기 위해 typescript를 얻는 적절한 방법은 무엇입니까?

다음은 간단한 예입니다.

의존

const myDep = (name: string) => name;
export default myDep;

test.ts

import * as dep from '../depenendency';
jest.mock('../dependency');

it('should do what I need', () => {
  //this throws ts error
  // Property mockReturnValueOnce does not exist on type (name: string)....
  dep.default.mockReturnValueOnce('return')
}

나는 이것이 매우 일반적인 사용 사례라고 생각하며 올바르게 입력하는 방법을 모르겠습니다. 어떤 도움이라도 대단히 감사하겠습니다!


1
내가 옳다는 것을 기억한다면 가져 오기 전에 조롱해야합니다. 처음 두 줄만 바꾸면됩니다. 하지만 이것에 대해 잘 모르겠습니다.
Thomas

3
@ ThomasKleßen ES6를 통해 가져온 모듈 import은 가져 오기 전에 코드를 넣어도 먼저 평가됩니다. 그래서 이것은 작동하지 않습니다.
mgol

jest.mock에 @Thomas 호출 코드의 상단에 게양된다 - 내가 생각 농담 마법 ( REF ) 그러나,이 몇 가지 함정, 때 예를 들어, 생성 모듈 공장 매개 변수 jest.mock ()를 호출 하므로 모의 기능의 이름을 asmock...
Tobi

답변:


98

유형 캐스팅을 사용할 수 있으며 test.ts다음과 같이 표시됩니다.

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는 jest.mock('../dependency');유형 변경을 인식하지 못 dep하므로 유형 캐스팅을 사용해야합니다. 가져온 dep것은 유형 정의가 아니기 때문에 typeof dep.default.

Jest와 TS로 작업하면서 찾은 다른 유용한 패턴은 다음과 같습니다.

가져온 요소가 클래스이면 다음과 같이 typeof를 사용할 필요가 없습니다.

import { SomeClass } from './SomeClass';

jest.mock('./SomeClass');

const mockedClass = <jest.Mock<SomeClass>>SomeClass;

이 솔루션은 일부 노드 네이티브 모듈을 모의해야 할 때도 유용합니다.

import { existsSync } from 'fs';

jest.mock('fs');

const mockedExistsSync = <jest.Mock<typeof existsSync>>existsSync;

jest 자동 모의를 사용하지 않고 수동 모의 생성을 선호하는 경우

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()모의 객체 인스턴스를 생성 TestedClassDependency합니다. 클래스, 유형 또는 인터페이스가 될 수 있습니다.


3
IntelliJ가 불평하기 때문에 jest.fn(() =>...대신 jest.fn<TestedClassDependency>(() =>...(jest.fn 후에 유형 캐스팅을 제거했습니다) 대신 사용해야 했습니다 . 그렇지 않으면이 답변이 도움이되었습니다. 내 package.json이 사용 : "@ 유형 / 농담": "^ 24.0.3"
A. 메이슨

무엇 않습니다 jest.mock('./SomeClass');위의 코드에서?
Reza 19

11
흠 그것은 마지막 TS 버전과 jest 24에서 더 이상 작동하지 않습니다 :(
Vincent


5
<jest.Mock<SomeClass>>SomeClass표현은 나를 위해 TS 오류를 생산하고있다 :Conversion of type 'typeof SomeClass' to type 'Mock<SomeClass, any>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Type 'typeof SomeClass' is missing the following properties from type 'Mock<SomeClass, any>': getMockName, mock, mockClear, mockReset, and 11 more.ts(2352)
the21st

61

여기에 설명 mockedts-jest것과 같은 도우미를 사용 하십시오.

// 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)
})

그리고 만약

  • 너는 사용한다 tslint
  • ts-jest 개발 의존성에 있습니다.

이 규칙을에 추가하십시오 tslint.json."no-implicit-dependencies": [true, "dev"]


다음은 ts-jest및 클래스 사용에 대한 몇 가지 예입니다 . github.com/tbinna/ts-jest-mock-examples 및이 게시물 : stackoverflow.com/questions/58639737/…
Tobi

4
이것은 가장 많이 득표 한 답변보다 훨씬 더 나은 답변입니다.
fakeplasticandroid

@Tobi 저장소의 테스트가 실패합니다
Kreator

@Kreator 헤드 업에 감사드립니다. 보고 된 것과 동일한 문제가 표시 됩니까? 아직 문제를 재현 할 수 없습니다.
Tobi

@Kreator가 방금 PR을 병합했습니다. 문제가 지속되면 알려주세요
Tobi

18

Mocked에 대한 유형 def 바로 위에 @ types / jest / index.d.ts의 패턴을 사용합니다 (515 행).

import { Api } from "../api";
jest.mock("../api");

const myApi: jest.Mocked<Api> = new Api() as any;
myApi.myApiMethod.mockImplementation(() => "test");

2
나는 확신 당신은 그냥 할 수있어const myApi = new Api() as jest.Mocked<Api>;
snowfrogdev

4
@neoflash : TypeScript 3.4의 엄격 모드가 아닙니다 jest.Mock<Api>. Api 유형이 . 당신은 함께 가야 const myApi = new Api() as any as jest.Mock<Api>하고 나는 위의 것이 이중 주장보다 조금 더 나아 보인다고 말하고 싶습니다.
paolostyle

@tuptus : 3.4에서 엄격 모드가 새로운가요? 이것에 관한 링크가 있습니까?
elmpp

@elmpp : 무슨 뜻인지 잘 모르겠습니다. "엄격 모드"란 "strict": truetsconfig.json에 있음을 의미 했습니다. 여기에는 noImplicitAny, strictNullChecks등과 같은 항목이 포함 되므로 개별적으로 true로 설정할 필요가 없습니다.
paolostyle

이해가 안 돼요. 왜 한 인스턴스의 메서드 만 스터 빙하고 myApi있습니까? 일반적으로 Api테스트되는 모듈 내에서 클래스에 의해 시작된 다른 모든 인스턴스를 스텁하지 않습니다 .
Ivan Wang

14

두 가지 솔루션이 있으며 둘 다 원하는 기능을 캐스팅합니다.

1) jest.MockedFunction 사용

import * as dep from './dependency';

jest.mock('./dependency');

const mockMyFunction = dep.myFunction as jest.MockedFunction<typeof dep.myFunction>;

2) jest.Mock 사용

import * as dep from './dependency';

jest.mock('./dependency');

const mockMyFunction = dep.default as jest.Mock;

이 두 솔루션에는 차이가 없습니다. 두 번째 것은 더 짧기 때문에 나는 그것을 사용하는 것이 좋습니다.

두 캐스팅 솔루션 모두 mockMyFunctionlike mockReturnValue또는 https://jestjs.io/docs/en/mock-function-api.html 에서 jest mock 함수를 호출 할 수 mockResolvedValue 있습니다.

mockMyFunction.mockReturnValue('value');

mockMyFunction 예상에 일반적으로 사용할 수 있습니다.

expect(mockMyFunction).toHaveBeenCalledTimes(1);

7

캐스트 as jest.Mock

단순히 함수를로 캐스팅하면 jest.Mock트릭을 수행 할 수 있습니다.

(dep.default as jest.Mock).mockReturnValueOnce('return')


6

다음은 jest@24.8.0ts-jest@24.0.2로 수행 한 작업입니다 .

출처:

class OAuth {

  static isLogIn() {
    // return true/false;
  }

  static getOAuthService() {
    // ...
  }
}

테스트:

import { OAuth } from '../src/to/the/OAuth'

jest.mock('../src/utils/OAuth', () => ({
  OAuth: class {
    public static getOAuthService() {
      return {
        getAuthorizationUrl() {
          return '';
        }
      };
    }
  }
}));

describe('createMeeting', () => {
  test('should call conferenceLoginBuild when not login', () => {
    OAuth.isLogIn = jest.fn().mockImplementationOnce(() => {
      return false;
    });

    // Other tests
  });
});

이것은 기본이 아닌 클래스를 모의하는 방법이며 정적 메서드입니다.

jest.mock('../src/to/the/OAuth', () => ({
  OAuth: class {
    public static getOAuthService() {
      return {
        getAuthorizationUrl() {
          return '';
        }
      };
    }
  }
}));

여기에 클래스 유형에서 jest.MockedClass이와 비슷한 유형으로 변환해야합니다 . 그러나 그것은 항상 오류로 끝납니다. 그래서 직접 사용했고 효과가있었습니다.

test('Some test', () => {
  OAuth.isLogIn = jest.fn().mockImplementationOnce(() => {
    return false;
  });
});

하지만 함수라면 조롱하고 타입 대화를 할 수 있습니다.

jest.mock('../src/to/the/Conference', () => ({
  conferenceSuccessDataBuild: jest.fn(),
  conferenceLoginBuild: jest.fn()
}));
const mockedConferenceLoginBuild = conferenceLoginBuild as 
jest.MockedFunction<
  typeof conferenceLoginBuild
>;
const mockedConferenceSuccessDataBuild = conferenceSuccessDataBuild as 
jest.MockedFunction<
  typeof conferenceSuccessDataBuild
>;

4

나는 이것을 발견했다 @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);
*/

참고 :const mockMyFunction = myFunction 다음과 같은 작업을 수행 mockFunction.mockReturnValue('foo')하면 변경 myFunction됩니다.

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


0

최근 라이브러리는 babel 플러그인으로이 문제를 해결했습니다 : https://github.com/userlike/joke

예:

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');
}

dep그리고 mockReturnValueOnce완전히 형식이 안전하다는 것을 알고 있어야합니다 . 맨 위에 tsserver는 depencency가져 와서 할당 되었음을 인식 dep하므로 tsserver가 지원하는 모든 자동 리팩토링도 작동합니다.

참고 : 라이브러리를 유지합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.