Làm cách nào tôi có thể giả định nhập mô-đun ES6 bằng Jest?


281

Tôi bắt đầu nghĩ rằng điều này là không thể, nhưng dù sao tôi cũng muốn hỏi.

Tôi muốn kiểm tra rằng một trong các mô-đun ES6 của tôi gọi một mô-đun ES6 khác theo một cách cụ thể. Với Jasmine thì điều này siêu dễ -

Mã ứng dụng:

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

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

Và mã kiểm tra:

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

Tương đương với Jest là gì? Tôi cảm thấy như đây là một điều đơn giản muốn làm, nhưng tôi đã xé tóc ra để cố gắng tìm ra nó.

Lần gần nhất tôi đến là bằng cách thay thế imports bằng requires và di chuyển chúng vào bên trong các bài kiểm tra / chức năng. Không phải trong số đó là những điều tôi muốn làm.

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

Đối với điểm thưởng, tôi muốn làm cho toàn bộ hoạt động khi chức năng bên trong dependency.jslà xuất mặc định. Tuy nhiên, tôi biết rằng gián điệp xuất khẩu mặc định không hoạt động ở Jasmine (hoặc ít nhất là tôi không bao giờ có thể làm cho nó hoạt động), vì vậy tôi cũng không hy vọng rằng điều đó cũng có thể xảy ra với Jest.


Dù sao tôi cũng đang sử dụng Babel cho dự án này, vì vậy tôi không ngại tiếp tục chuyển mã importsang requires cho đến bây giờ. Cảm ơn cho những người đứng đầu mặc dù.
Cam Jackson

Điều gì xảy ra nếu tôi có ts lớp A và nó gọi một số chức năng, hãy nói
doS Something

đối với những người muốn khám phá vấn đề này nhiều hơn github.com/facebook/jest/issues/936
omeralper

Câu trả lời:


219

Tôi đã có thể giải quyết điều này bằng cách sử dụng một vụ hack liên quan import *. Nó thậm chí hoạt động cho cả xuất khẩu được đặt tên và mặc định!

Đối với xuất khẩu có tên:

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

Hoặc để xuất mặc định:

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

Như Mihai Damian hoàn toàn chỉ ra bên dưới, điều này đang làm thay đổi đối tượng mô-đun dependency, và do đó nó sẽ 'rò rỉ' qua các thử nghiệm khác. Vì vậy, nếu bạn sử dụng phương pháp này, bạn nên lưu trữ giá trị ban đầu và sau đó đặt lại sau mỗi lần kiểm tra. Để thực hiện điều này dễ dàng với Jest, hãy sử dụng phương thức spyOn () thay jest.fn()vì vì nó hỗ trợ dễ dàng khôi phục giá trị ban đầu của nó, do đó tránh trước khi 'rò rỉ' được đề cập.


Cám ơn vì đã chia sẻ. Tôi nghĩ rằng kết quả ròng tương tự như thế này - nhưng điều này có thể sạch hơn - stackoverflow.com/a/38414160/1882064
arcseldon

64
Điều này hoạt động, nhưng nó có thể không phải là một thực hành tốt. Những thay đổi đối với các đối tượng nằm ngoài phạm vi của thử nghiệm dường như vẫn tồn tại giữa các thử nghiệm. Điều này sau đó có thể dẫn đến kết quả bất ngờ trong các thử nghiệm khác.
Mihai Damian

10
Thay vì sử dụng jest.fn (), bạn có thể sử dụng jest.spyOn () để bạn có thể khôi phục phương thức ban đầu sau đó, vì vậy nó không bị chảy vào các thử nghiệm khác. Tôi tìm thấy bài viết hay về các cách tiếp cận khác nhau ở đây (jest.fn, jest.mock và jest.spyOn): Medium.com/@rickhanlonii/under Hiểu-jest-mocks-f0046c68e53c .
Martinsos

2
Chỉ cần một lưu ý: nếu dependencynó nằm trên cùng một tệp myModule, nó sẽ không hoạt động.
Lu Tran

2
Tôi nghĩ rằng điều này sẽ không hoạt động với Bản mô tả, đối tượng bạn đang thay đổi là chỉ đọc.
adredx

171

Bạn phải chế giễu mô-đun và tự mình thiết lập gián điệp:

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
Điều này có vẻ không đúng. Tôi nhận được: babel-plugin-jest-hoist: The second argument of jest.mock must be a function.Vì vậy, mã thậm chí không biên dịch.
Cam Jackson

3
Xin lỗi, tôi đã cập nhật mã của mình. Cũng xin lưu ý rằng đường dẫn trong jest.mockcó liên quan đến tệp thử nghiệm.
Andreas Köberle

1
Điều này đã làm việc cho tôi, tuy nhiên, không phải khi sử dụng xuất khẩu mặc định.
Iris Schaffer

4
@IrisSchaffer để có tác phẩm này với xuất mặc định, bạn cần thêm __esModule: truevào đối tượng giả. Đó là cờ nội bộ được sử dụng bởi mã được mã hóa để xác định xem đó là mô đun es6 được phiên mã hay mô đun chung.
Julian Lumpe

24
Mocking xuất khẩu mặc định: jest.mock('../dependency', () => ({ default: jest.fn() }))
Neob91

50

Để giả định xuất khẩu mặc định mô-đun phụ thuộc ES6 bằng jest:

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

Các tùy chọn khác không hoạt động cho trường hợp của tôi.


6
cách tốt nhất để dọn dẹp thứ này là gì nếu tôi chỉ muốn làm một bài kiểm tra? bên trong sau khi nào? `` `` afterEach (() => {jest.unmock (../ phụ thuộc ');}) `` ``
nxmohamad

1
@falsarella doMock thực sự hoạt động trong trường hợp đó? Tôi đang gặp vấn đề rất giống nhau và không có gì khi tôi cố gắng jest.doMock trong thử nghiệm cụ thể, trong đó jest.mock cho toàn bộ mô-đun đang hoạt động chính xác
Progress1ve

1
@ Progress1ve bạn cũng có thể thử sử dụng jest.mock với mockImcellenceationOnce
falsarella

1
Yup, đó là một đề xuất hợp lệ, tuy nhiên, yêu cầu bài kiểm tra phải là đề xuất đầu tiên và tôi không phải là người thích viết bài kiểm tra theo cách như vậy. Tôi đã khắc phục những vấn đề đó bằng cách nhập mô-đun bên ngoài và sử dụng spyOn trên các chức năng cụ thể.
Progress1ve

1
@ Progress1ve hmm Tôi có nghĩa là đặt mockImcellenceationOnce bên trong mỗi bài kiểm tra cụ thể ... dù sao, tôi rất vui vì bạn đã tìm thấy một giải pháp :)
falsarella 23/218

38

Thêm nhiều hơn vào câu trả lời của Andreas. Tôi gặp vấn đề tương tự với mã ES6 nhưng không muốn thay đổi nhập khẩu. Trông có vẻ hacky. Vì vậy, tôi đã làm điều này

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

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

Và đã thêm phụ thuộc.js vào thư mục "__ mocks __" song song với Depency.js. Điều này làm việc cho tôi. Ngoài ra, điều này cho tôi tùy chọn trả về dữ liệu phù hợp từ việc thực hiện giả. Hãy chắc chắn rằng bạn đưa ra đường dẫn chính xác đến mô-đun bạn muốn giả.


Cảm ơn vì điều đó. Sẽ thử nó. Cũng thích giải pháp này - stackoverflow.com/a/38414160/1882064
arcseldon

Điều tôi thích về cách tiếp cận này là nó cung cấp cho bạn khả năng cung cấp một bản giả thủ công cho tất cả các dịp mà bạn muốn mô phỏng một mô-đun cụ thể. Tôi ví dụ, có một người trợ giúp dịch thuật, được sử dụng ở nhiều nơi. Các __mocks__/translations.jstập tin chỉ đơn giản là mặc xuất khẩu jest.fn()trong một cái gì đó như:export default jest.fn((id) => id)
Iris Schaffer

Bạn cũng có thể sử dụng jest.genMockFromModuleđể tạo giả từ các mô-đun. facebook.github.io/jest/docs/ cường
Varunkumar Nagarajan

2
Một điều cần lưu ý là các mô-đun ES6 được mô phỏng thông qua export default jest.genMockFromModule('../dependency')sẽ có tất cả các chức năng của chúng được gán dependency.defaultsau khi gọi 'jest.mock (' .. phụ thuộc '), nhưng nếu không thì hoạt động như mong đợi.
jhk

7
Thử nghiệm của bạn trông như thế nào? Đó dường như là một phần quan trọng của câu trả lời. expect(???)
đá

14

Chuyển tiếp nhanh đến năm 2020, tôi thấy liên kết này là giải pháp. chỉ sử dụng cú pháp mô-đun 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

Ngoài ra, một điều bạn cần biết (điều này khiến tôi mất một lúc để tìm hiểu) là bạn không thể gọi jest.mock () trong bài kiểm tra; bạn phải gọi nó ở cấp cao nhất của mô-đun. Tuy nhiên, bạn có thể gọi mockImcellenceation () bên trong các thử nghiệm riêng lẻ nếu bạn muốn thiết lập các giả định khác nhau cho các thử nghiệm khác nhau.


5

Câu hỏi đã được trả lời nhưng bạn có thể giải quyết nó như thế này:

phụ thuộc.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
  });
});

Nhưng "yêu cầu" là cú pháp CommonJS - OP đã hỏi về Mô-đun ES6
Andy

@Andy cảm ơn bình luận của bạn, tôi đã cập nhật câu trả lời của tôi. BTW điều tương tự trong logic.
Slim

2

Tôi đã giải quyết điều này theo cách khác. Giả sử bạn có lệ thuộc.js của bạn

export const myFunction = () => { }

Tôi tạo một tệp depdency.mock.js bên cạnh đó với nội dung sau:

export const mockFunction = jest.fn();

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

và trong thử nghiệm, trước khi tôi nhập tệp có phụ thuộc tôi sử dụng:

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

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

    functionThatCallsDep();

    expect(mockFunction).toHaveBeenCalled();

})
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.