Javascript 如何使用ES6模块模拟单元测试的依赖关系

Javascript 如何使用ES6模块模拟单元测试的依赖关系,javascript,unit-testing,ecmascript-6,traceur,jestjs,Javascript,Unit Testing,Ecmascript 6,Traceur,Jestjs,我试图使用webpack+traceur来处理Ecmascript 6模块,以将其传输到ES5 CommonJS,但我在成功地对它们进行单元测试时遇到了困难 我曾尝试使用Jest+traceur预处理器,但自动模拟和依赖项名称似乎变得异常,而且我似乎无法让sourceMaps与Jest和节点检查器调试一起工作 有更好的框架来单元测试ES6模块吗?我实际上是通过放弃玩笑,使用Karma+Jasmine+Webpack,并使用模拟依赖项来实现这一点的。如果你使用Webpack,另一个比rewire更

我试图使用webpack+traceur来处理Ecmascript 6模块,以将其传输到ES5 CommonJS,但我在成功地对它们进行单元测试时遇到了困难

我曾尝试使用Jest+traceur预处理器,但自动模拟和依赖项名称似乎变得异常,而且我似乎无法让sourceMaps与Jest和节点检查器调试一起工作


有更好的框架来单元测试ES6模块吗?

我实际上是通过放弃玩笑,使用Karma+Jasmine+Webpack,并使用模拟依赖项来实现这一点的。如果你使用Webpack,另一个比rewire更灵活的选项是

例如,在与Webpack捆绑的测试中:

describe('when an alert is dismissed', () => {

  // Override Alert as we need to mock dependencies for these tests
  let Alert, mockPubSub

  beforeEach(() => {
    mockPubSub = {}
    Alert =  require('inject!./alert')({
      'pubsub-js': mockPubSub
    }).Alert
  })

  it('should publish \'app.clearalerts\'', () => {
    mockPubSub.publish = jasmine.createSpy()
    [...]
    expect(mockPubSub.publish).toHaveBeenCalled()
  })
})

InjectLoader,以与proxyquire类似的方式,至少允许在导入之前插入依赖项,而在Rewre中,必须先导入然后重新布线,这使得模拟某些组件(例如,具有一些初始化的组件)不可能。

我已开始在测试中使用
import*作为obj
样式,它将模块中的所有导出作为对象的属性导入,然后可以对该对象进行模拟。我发现这比使用诸如重新布线、proxyquire或任何类似的技术要干净得多

我不能为问题中使用的框架traceur说话,但我发现这与我的Karma、Jasmine和Babel设置有关,我在这里发布它,因为这似乎是这类问题中最流行的问题

我经常在需要模拟Redux操作时使用此策略。下面是一个简短的例子:

import * as exports from 'module-you-want-to-mock';
import SystemUnderTest from 'module-that-uses-above-module';

describe('your module', () => {
  beforeEach(() => {
    spyOn(exports, 'someNamedExport');  // mock a named export
    spyOn(exports, 'default');          // mock the default export
  });
  // ... now the above functions are mocked
});

您好,您可以使用proxyquire:

import assert from 'assert';
import sinon from 'sinon';
import Proxyquire from 'proxyquire';

let proxyquire = Proxyquire.noCallThru(),
    pathModelLoader = './model_loader';

describe('ModelLoader module.', () => {
    it('Should load all models.', () => {
        let fs, modelLoader, ModelLoader, spy, path;
        fs = {
            readdirSync(path) {
                return ['user.js'];
            }
        };
        path = {
            parse(data) {
                return {name: 'user'};
            }
        };
        ModelLoader = proxyquire(pathModelLoader, {'fs': fs, 'path': path});
        modelLoader = new ModelLoader.default();
        spy = sinon.spy(modelLoader, 'loadModels');
        modelLoader.loadModels();
        assert(spy.called);
    });
});

Proxyquire将帮助您,但它无法与现代webpack+ES6模块(即“别名”)一起工作

那将不起作用。
只要你能模仿fs,你就不能模仿减缩器。
在任何Web包或babel转换之后,必须指定依赖项的
real
名称。通常-相对于模块1位置的名称。可以是“../../../shared/core/reducers”。也许不是

解决方案有下降趋势-(稳定,基于proxyquire的分支)或(不太稳定,可以在原始proxyquire上运行)


他们两个都很好用,并且会以一种特殊的方式模仿任何ES6模块(他们都很好)(这是一种很好的方式)

我不知道Traceur,但与jest配合得很好,并且有一个。谢谢James,我会查看的。它看起来确实像是6to5文件,让人更容易阅读,并且涵盖了我需要的所有ES6功能,所以我可能会这样做。另外,您是否能够使用节点检查器通过jest获取sourceMaps进行调试?我还建议使用Babel.js将您的ES6代码传输到ES5中。在传输测试或实现代码时,我没有遇到任何问题。如果您使用的是babel,那么您也可以简单地模拟模块,而不是使用专用的网页或browserify加载程序。函数中的变量不能通过重新布线来更改。这是受JavaScript约束的,不能用rewire来规避。我很高兴这个建议对你有用。。。但这是如何被接受的答案呢?“不要用那个”不一定有用:(是的,但这仅在使用
require
而不是
import..as..
syntaxNot true时有效。我成功地将其与ES6导入语法一起使用。我想这取决于您的设置,但我使用的是带Babel loader的Babel。它适用于
require
和ES6导入。唯一需要的地方是模拟依赖项。)在测试中。嗨@djskinner。我正在尝试让InjectLoader与babel、webpack和karam一起运行,但遇到了问题。您是否介意添加
karma.config.js
(如果您使用
webpack.config
文件)感谢您的回答。提前感谢。请看一看好的起点。这主要是关于代码覆盖率,但基本配置很好,即使您对代码覆盖率部分不感兴趣。至于注入加载器,我不需要任何额外的配置,只需
npm I inject loader
require(inject!…)
如何将其用于amd模块?我得到“模块名称”inject!src/qux_unnormalized2“尚未为上下文加载”这种模式或技术应该被更好地记录下来,因为它是模拟es6模块的一种有用方法,而不是依赖于第三方依赖关系。我希望看到,随着es6变得更加主流,与单元测试相关,在导入模块而不是使用
导入时,这种模式在示例中更常见{someExport}来自module
notation。由于您提到了redux操作,因此值得指出的是,必须密切注意操作何时绑定到连接的组件。将SPIE应用到已绑定的函数可能没有效果。了解调用react-redux的connect()的不同方法函数是我在不失去理智的情况下实现这一点的关键:如何防止测试互相渗透?根据我的经验,模拟导入会导致以后的测试使用模拟而不是实际实例,反之亦然。@user831865根据您的测试框架,模拟的范围应该是
descriptionbe
块中定义了模拟。在不了解您的特定设置的情况下,很难说。@user831865如果您使用的是sinon,
sinon。sandbox
可以防止模拟流到其他测试中。
import fs from 'fs';
import reducers from 'core/reducers';
...
proxyquire('../module1', {
  'fs': mockFs,  // this gonna work
  'core/reducers': mockReducers // what about this?
});