Jest를 사용하여 ES6 모듈 가져 오기를 어떻게 조롱 할 수 있습니까?


281

이것이 불가능하다고 생각하기 시작했지만 어쨌든 묻고 싶습니다.

ES6 모듈 중 하나가 특정 방식으로 다른 ES6 모듈을 호출하는지 테스트하고 싶습니다. Jasmine을 사용하면 매우 쉽습니다.

앱 코드 :

// myModule.js
import dependency from './dependency';

export default (x) => {
  dependency.doSomething(x * 2);
}

그리고 테스트 코드 :

//myModule-test.js
import myModule from '../myModule';
import dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    spyOn(dependency, 'doSomething');

    myModule(2);

    expect(dependency.doSomething).toHaveBeenCalledWith(4);
  });
});

Jest와 동등한 것은 무엇입니까? 나는 이것이 그렇게하고 싶은 단순한 일이라고 생각하지만, 그것을 알아 내려고 노력하면서 머리카락을 찢어 버렸습니다.

내가 가장 가까운 것은 imports를 requires 로 바꾸고 테스트 / 함수 내부로 옮기는 것입니다. 어느 쪽도 내가하고 싶은 일이 아닙니다.

// myModule.js
export default (x) => {
  const dependency = require('./dependency'); // yuck
  dependency.doSomething(x * 2);
}

//myModule-test.js
describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    jest.mock('../dependency');

    myModule(2);

    const dependency = require('../dependency'); // also yuck
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

보너스 포인트의 경우 내부 함수 dependency.js가 기본 내보내기 일 때 모든 것이 작동하도록하고 싶습니다 . 그러나 기본 수출에 대한 감시가 Jasmine에서 작동하지 않거나 적어도 작동하지 않을 수 있다는 것을 알고 있으므로 Jest에서도 가능하다는 희망을 가지고 있지 않습니다.


어쨌든이 프로젝트에 Babel을 사용하고 있으므로 imports를 계속해서 번역하지 않아도 require됩니다. 그래도 감사합니다.
Cam Jackson

내가 ts 클래스 A를 가지고 있고 함수를 호출하면 클래스 B의 doSomething ()이라고 말할 수 있습니다. 클래스 A가 클래스 B 함수의 조롱 된 버전을 호출하도록 어떻게 조롱 할 수 있습니까? doSomething ()
KAILASH 요게 슈

이 문제를 더 많이 발견하려는 사람들을 위해 github.com/facebook/jest/issues/936을
omeralper

답변:


221

관련 해킹을 사용하여이 문제를 해결할 수있었습니다 import *. 명명 된 내보내기와 기본 내보내기 모두에서 작동합니다!

명명 된 내보내기의 경우 :

// dependency.js
export const doSomething = (y) => console.log(y)

// myModule.js
import { doSomething } from './dependency';

export default (x) => {
  doSomething(x * 2);
}

// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.doSomething = jest.fn(); // Mutate the named export

    myModule(2);

    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

또는 기본 내보내기의 경우 :

// dependency.js
export default (y) => console.log(y)

// myModule.js
import dependency from './dependency'; // Note lack of curlies

export default (x) => {
  dependency(x * 2);
}

// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.default = jest.fn(); // Mutate the default export

    myModule(2);

    expect(dependency.default).toBeCalledWith(4); // Assert against the default
  });
});

Mihai Damian이 아래에서 올바르게 지적했듯이 이것은의 모듈 객체를 변경 dependency하고 있으므로 다른 테스트로 '누설'됩니다. 따라서이 방법을 사용하면 원래 값을 저장 한 다음 각 테스트 후에 다시 설정해야합니다. Jest로 쉽게 수행하려면 spyOn ()을 사용하십시오.jest.fn() 원래 값 복원을 쉽게 지원하므로 앞에서 언급 한 '누출'을 피하기 때문에 메서드를 사용하십시오 .


공유해 주셔서 감사합니다. 나는 최종 결과는 다음과 유사하다고 생각 - 그러나 이것은 청소기 수 있습니다 - stackoverflow.com/a/38414160/1882064을
arcseldon

64
이것은 효과가 있지만 아마도 좋은 습관은 아닙니다. 테스트 범위를 벗어난 객체에 대한 변경은 테스트간에 지속되는 것으로 보입니다. 나중에 다른 테스트에서 예기치 않은 결과가 발생할 수 있습니다.
Mihai Damian

10
jest.fn ()을 사용하는 대신 jest.spyOn ()을 사용하여 나중에 원래 메소드를 복원 할 수 있으므로 다른 테스트에서 번지지 않습니다. 다른 접근법 (jest.fn, jest.mock 및 jest.spyOn)에 대한 좋은 기사를 찾았습니다 : medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c .
Martinsos

2
참고 사항 : dependency이와 같은 파일 myModule에 있으면 작동하지 않습니다.
Lu Tran

2
변경하려는 객체가 읽기 전용 인 Typescript에서는 작동하지 않는다고 생각합니다.
adredx

172

모듈을 조롱하고 스파이를 직접 설정해야합니다.

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency', () => ({
  doSomething: jest.fn()
}))

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

4
이것은 옳지 않은 것 같습니다. 나는 얻는다 : babel-plugin-jest-hoist: The second argument of jest.mock must be a function.그래서 코드는 컴파일되지 않는다.
Cam Jackson

3
죄송합니다. 코드를 업데이트했습니다. 또한 경로 jest.mock는 테스트 파일과 관련이 있습니다.
Andreas Köberle

1
그러나 이것은 기본 내보내기를 사용할 때가 아니 었습니다.
아이리스 셰퍼

4
@IrisSchaffer @ 기본 내보내기로이 작업 __esModule: true을 수행하려면 모의 객체 에 추가해야 합니다. 이것은 코드가 변환 된 es6 모듈인지 commonjs 모듈인지를 판별하기 위해 변환 된 코드에서 사용하는 내부 플래그입니다.
Johannes Lumpe

24
jest.mock('../dependency', () => ({ default: jest.fn() }))
모의

50

jest를 사용하여 ES6 종속성 모듈 기본 내보내기를 조롱하려면 다음을 수행하십시오.

import myModule from '../myModule';
import dependency from '../dependency';

jest.mock('../dependency');

// If necessary, you can place a mock implementation like this:
dependency.mockImplementation(() => 42);

describe('myModule', () => {
  it('calls the dependency once with double the input', () => {
    myModule(2);

    expect(dependency).toHaveBeenCalledTimes(1);
    expect(dependency).toHaveBeenCalledWith(4);
  });
});

다른 옵션은 제 경우에는 효과가 없었습니다.


6
한 번만 시험 해보고 싶다면 이것을 정리하는 가장 좋은 방법은 무엇입니까? 안에? ````afterEach (() => {jest.unmock (../ dependency ');})````
nxmohamad

1
@falsarella이 경우 doMock은 실제로 작동합니까? 나는 매우 비슷한 문제를 겪고 있으며 특정 테스트 내에서 jest.doMock을 시도 할 때 아무것도하지 않습니다. 전체 모듈에 대한 jest.mock이 올바르게 작동합니다
Progress1ve

1
@ Progress1ve 당신은 mockImplementationOnce와 함께 jest.mock을 사용해 볼 수 있습니다
falsarella

1
그래, 그것은 유효한 제안이지만, 테스트가 첫 번째이어야하며 나는 그런 식으로 테스트를 작성하는 팬이 아닙니다. 외부 모듈을 가져오고 특정 기능에서 spyOn을 사용하여 이러한 문제를 해결했습니다.
Progress1ve

1
@ Progress1ve 흠 어쨌든, 난 당신이 솔루션 :) 발견했습니다 행복 해요 ... 각각의 특정 테스트 내부 mockImplementationOnce을 배치하는 것을 의미
falsarella

38

Andreas 답변에 더 추가. ES6 코드와 동일한 문제가 있었지만 가져 오기를 변경하고 싶지 않았습니다. 해키처럼 보였다. 그래서 나는 이것을했다

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency');

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
  });
});

그리고 "__ mocks __"폴더에 dependency.js와 병행하여 dependency.js를 추가했습니다. 이것은 나를 위해 일했습니다. 또한 이것은 모의 구현에서 적합한 데이터를 반환 할 수있는 옵션을 제공했습니다. 조롱하려는 모듈의 올바른 경로를 제공하십시오.


고마워 시도해 볼 것입니다. 또한이 솔루션을 좋아 하냐 - stackoverflow.com/a/38414160/1882064
arcseldon

이 접근법에 대해 내가 좋아하는 것은 특정 모듈을 조롱하려는 모든 경우에 대해 하나의 수동 모의를 제공 할 수 있다는 것입니다. 예를 들어, 많은 곳에서 사용되는 번역 도우미가 있습니다. __mocks__/translations.js파일은 단순히 수출 기본적으로 jest.fn()같은 뭔가를 :export default jest.fn((id) => id)
아이리스 샤퍼을

당신은 또한 사용할 수 있습니다 jest.genMockFromModule모듈에서 모의 ​​객체를 생성 데 . facebook.github.io/jest/docs/…
Varunkumar Nagarajan

2
주목해야 할 것은 mocked ES6 모듈 export default jest.genMockFromModule('../dependency')dependency.default`jest.mock ( '.. dependency')를 호출 한 후에 할당 된 모든 기능을 갖지만 예상대로 동작한다는 것입니다.
jhk

7
테스트 어설 션은 어떻게 생겼습니까? 그것은 대답의 중요한 부분처럼 보입니다. expect(???)
stone

14

2020 년으로 빨리 넘어 가면서이 링크가 해결책이라는 것을 알았습니다. ES6 모듈 구문 만 사용 https://remarkablemark.org/blog/2018/06/28/jest-mock-default-named-export/

// esModule.js
export default 'defaultExport';
export const namedExport = () => {};

// esModule.test.js
jest.mock('./esModule', () => ({
  __esModule: true, // this property makes it work
  default: 'mockedDefaultExport',
  namedExport: jest.fn(),
}));

import defaultExport, { namedExport } from './esModule';
defaultExport; // 'mockedDefaultExport'
namedExport; // mock function

또한 당신이 알아야 할 한 가지는 (내가 알아내는 데 시간이 걸렸다) 테스트 내에서 jest.mock ()을 호출 할 수 없다는 것입니다. 모듈의 최상위 레벨에서 호출해야합니다. 그러나 테스트마다 다른 모의 객체를 설정하려면 개별 테스트 내에서 mockImplementation ()을 호출 할 수 있습니다.


5

질문에 이미 답변되었지만 다음과 같이 해결할 수 있습니다.

dependency.js

const doSomething = (x) => x
export default doSomething;

myModule.js :

import doSomething from "./dependency";

export default (x) => doSomething(x * 2);

myModule.spec.js :

jest.mock('../dependency');
import doSomething from "../dependency";
import myModule from "../myModule";

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    doSomething.mockImplementation((x) => x * 10)

    myModule(2);

    expect(doSomething).toHaveBeenCalledWith(4);
    console.log(myModule(2)) // 40
  });
});

그러나 "필수"는 CommonJS 구문입니다. OP는 ES6 모듈에 대해 질문했습니다
Andy

@Andy 귀하의 의견에 감사드립니다. 내 답변을 업데이트했습니다. 논리에서 BTW도 마찬가지입니다.
슬림

2

나는 이것을 다른 방법으로 해결했다. 의존성이 있다고 가정 해 봅시다.

export const myFunction = () => { }

다음 내용으로 depdency.mock.js 파일을 만듭니다.

export const mockFunction = jest.fn();

jest.mock('dependency.js', () => ({ myFunction: mockFunction }));

테스트에서 내가 사용하는 depedency가있는 파일을 가져 오기 전에 :

import { mockFunction } from 'dependency.mock'
import functionThatCallsDep from './tested-code'

it('my test', () => {
    mockFunction.returnValue(false);

    functionThatCallsDep();

    expect(mockFunction).toHaveBeenCalled();

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