如何模拟ES6模块的导入? [英] How can I mock the imports of an ES6 module?

查看:91
本文介绍了如何模拟ES6模块的导入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下ES6模块:

export function getDataFromServer() {
  return ...
}


文件 widget.js


File widget.js

import { getDataFromServer } from 'network.js';

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

  render() {
    ...
  }
}

我正在寻找一种使用 getDataFromServer 的模拟实例测试Widget的方法。如果我使用单独的< script> s而不是ES6模块(例如在Karma中),则可以这样编写测试:

I'm looking for a way to test Widget with a mock instance of getDataFromServer. If I used separate <script>s instead of ES6 modules, like in Karma, I could write my test like:

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模块(例如使用摩卡 + Babel ),我会写类似的东西:

However, if I'm testing ES6 modules individually outside of a browser (like with Mocha + Babel), I would write something like:

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

好,但是现在 getDataFromServer 在<$ c中不可用$ c> window (好吧,根本没有 window ),我不知道将东西直接注入<$ c的方法$ c> widget.js 自己的作用域。

Okay, but now getDataFromServer is not available in window (well, there's no window at all), and I don't know a way to inject stuff directly into widget.js's own scope.


  1. 是否可以访问 widget.js 的范围,或者至少用我自己的代码替换其导入内容?

  2. 如果没有,如何使小部件可测试?

  1. Is there a way to access the scope of widget.js, or at least replace its imports with my own code?
  2. If not, how can I make Widget testable?




我考虑过的东西


a。


widget.js 删除所有导入,并期望调用方提供dep。


Stuff I considered:

a. Manual dependency injection.

Remove all imports from widget.js and expect the caller to provide the deps.

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

我很不喜欢这样破坏Widget的公共界面并暴露实施细节。

I'm very uncomfortable with messing up Widget's public interface like this and exposing implementation details. No go.

类似的东西:

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 。我对此感到不安,但这是到目前为止我最好的主意。

This is less invasive, but it requires me to write a lot of boilerplate for each module, and there's still a risk of me using getDataFromServer instead of deps.getDataFromServer all the time. I'm uneasy about it, but that's my best idea so far.

推荐答案

我已经开始使用<$ c $在我的测试中,c> import * as obj 样式,该样式将从模块中导入所有导出作为对象的属性,然后可以对其进行模拟。我发现这比使用诸如rewire或proxyquire或任何类似技术之类的方法要干净得多。例如,在需要模拟Redux操作时,我经常这样做。在上面的示例中,我可能会使用以下示例:

I've started employing the import * as obj style within my tests, which imports all exports from a module as properties of an object which can then be mocked. I find this to be a lot cleaner than using something like rewire or proxyquire or any similar technique. I've done this most often when needing to mock Redux actions, for example. Here's what I might use for your example above:

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从'./network'会产生 {default:getDataFromServer} ,您可以模拟network.default。

If your function happens to be a default export, then import * as network from './network' would produce {default: getDataFromServer} and you can mock network.default.

这篇关于如何模拟ES6模块的导入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆