javascript set - 如何模擬ES6模塊的導入?




2 Answers

我已經開始在我的測試中使用import * as obj樣式,它將一個模塊的所有導出作為可以被模擬的對象的屬性導入。 我覺得這比使用rewire或者proxyquire或者其他類似的技術更清潔。 例如,當我需要模擬Redux動作時,我經常這樣做。 以下是我可能用於上述示例的內容:

import * as network from 'network.js';

describe("widget", function() {
  it("should do stuff", function() {
    let getDataFromServer = spyOn(network, "getDataFromServer").andReturn("mockData")
    let widget = new Widget();
    expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
    expect(otherStuff).toHaveHappened();
  });
});

如果你的函數碰巧是一個默認導出,那麼import * as network from './network'會產生{default: getDataFromServer} ,你可以模擬network.default。

title w3school

我有以下ES6模塊:

network.js

export function getDataFromServer() {
  return ...
}

widget.js

import { getDataFromServer } from 'network.js';

export class Widget() {
  constructor() {
    getDataFromServer("dataForWidget")
    .then(data => this.render(data));
  }

  render() {
    ...
  }
}

我正在尋找一種使用getDataFromServer的模擬實例來測試Widget的方法。 如果我使用單獨的<script>代替ES6模塊,就像在Karma中一樣,我可以編寫我的測試:

describe("widget", function() {
  it("should do stuff", function() {
    let getDataFromServer = spyOn(window, "getDataFromServer").andReturn("mockData")
    let widget = new Widget();
    expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
    expect(otherStuff).toHaveHappened();
  });
});

但是,如果我在瀏覽器外單獨測試ES6模塊(如使用Mocha + babel),我會寫如下內容:

import { Widget } from 'widget.js';

describe("widget", function() {
  it("should do stuff", function() {
    let getDataFromServer = spyOn(?????) // How to mock?
    .andReturn("mockData")
    let widget = new Widget();
    expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
    expect(otherStuff).toHaveHappened();
  });
});

好吧,但現在getDataFromServerwindow不可用( widget.js ,根本沒有window ),我不知道一種方法直接將東西注入到widget.js自己的作用域中。

那麼我從哪裡去?

  1. 有沒有辦法訪問widget.js的作用域,或者至少用我自己的代碼替換它的導入?
  2. 如果沒有,我怎樣才能讓Widget可測試的?

我認為的東西:

一個。 手動依賴注入。

widget.js刪除所有導入,並期望調用者提供代碼。

export class Widget() {
  constructor(deps) {
    deps.getDataFromServer("dataForWidget")
    .then(data => this.render(data));
  }
}

我很不舒服地搞亂Widget的這個公共接口,並暴露實現細節。 不行。

灣 公開進口以允許嘲笑他們。

就像是:

import { getDataFromServer } from 'network.js';

export let deps = {
  getDataFromServer
};

export class Widget() {
  constructor() {
    deps.getDataFromServer("dataForWidget")
    .then(data => this.render(data));
  }
}

然後:

import { Widget, deps } from 'widget.js';

describe("widget", function() {
  it("should do stuff", function() {
    let getDataFromServer = spyOn(deps.getDataFromServer)  // !
      .andReturn("mockData");
    let widget = new Widget();
    expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
    expect(otherStuff).toHaveHappened();
  });
});

這是侵入性較小,但要求我為每個模塊編寫大量樣板,並且仍然存在使用getDataFromServer而不是deps.getDataFromServer的風險。 我對此感到不安,但這是我迄今為止最好的想法。




我發現這個語法正在工作:

我的模塊:

// mymod.js
import shortid from 'shortid';

const myfunc = () => shortid();
export default myfunc;

我的模塊的測試代碼:

// mymod.test.js
import myfunc from './mymod';
import shortid from 'shortid';

jest.mock('shortid');

describe('mocks shortid', () => {
  it('works', () => {
    shortid.mockImplementation(() => 1);
    expect(myfunc()).toEqual(1);
  });
});

請參閱文檔




Related

javascript unit-testing mocha ecmascript-6