如何模拟ES6模块的进口? [英] How to mock the imports of an ES6 module?

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

问题描述

我有以下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(){
...
}
}

我正在寻找一种方法来测试Widget的模拟实例 getDataFromServer 。如果我使用单独的< 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(?????)/ /如何模拟
.andReturn(mockData)
让widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith(dataForWidget);
expect otherstuff).toHaveHappened();
});
});

好的,但现在 getDataFromServer 不可用在窗口(好吧,根本没有$ code>窗口),我不知道一种直接注入东西的方法进入 widget.js 自己的范围。



那么我从哪里去?




  1. 有没有办法访问 widget.js 的范围,或至少替换使用我自己的代码导入?

  2. 如果没有,我该如何使 Widget testable? strong>






我认为:



a。手动依赖注入



widget.js 中删除​​所有导入,并期望调用者提供deps。 p>

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

我很不舒服Widget的公共界面像这样并揭示实现细节。不去。






b。

 从'network.js'导入{getDataFromServer}; 

export let deps = {
getDataFromServer
};

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

然后:

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

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

这是一个较少的侵入性,但要求我为每个模块编写大量的样板,还有一个所有的时间我使用 getDataFromServer 而不是 deps.getDataFromServer 的风险。我很不安,但这是迄今为止最好的想法。

解决方案

我已经开始使用 import * as obj 样式在我的测试中,它导入从模块的所有导出作为可以被嘲笑的对象的属性。我发现这比使用诸如重新连线或代码或类似技术的东西要好很多。例如,当需要模拟Redux操作时,我最常做到这一点。以下是我上面可以使用的例子:

  import * as'network.js'; 

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

如果您的功能恰好是默认导出,则 import *作为网络从'./network'将产生 {default:getDataFromServer} ,你可以模拟network.default。


I have the following ES6 modules:

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() {
    ...
  }
}

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

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

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.

So where do I go from here?

  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?


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

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


b. Expose the imports to allow mocking them.

Something like:

import { getDataFromServer } from 'network.js';

export let deps = {
  getDataFromServer
};

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

then:

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

This is less invasive but 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.

解决方案

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

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天全站免登陆