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

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

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에서도 가능하다는 희망을 가지고 있지 않습니다.



답변

관련 해킹을 사용하여이 문제를 해결할 수있었습니다 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() 원래 값 복원을 쉽게 지원하므로 앞에서 언급 한 ‘누출’을 피하기 때문에 메서드를 사용하십시오 .


답변

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

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


답변

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

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


답변

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를 추가했습니다. 이것은 나를 위해 일했습니다. 또한 이것은 모의 구현에서 적합한 데이터를 반환 할 수있는 옵션을 제공했습니다. 조롱하려는 모듈의 올바른 경로를 제공하십시오.


답변

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 ()을 호출 할 수 있습니다.


답변

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

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


답변

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

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();

})