如何使用 ES6 模块模拟单元测试的依赖关系
Posted
技术标签:
【中文标题】如何使用 ES6 模块模拟单元测试的依赖关系【英文标题】:How to mock dependencies for unit tests with ES6 Modules 【发布时间】:2015-02-04 01:04:26 【问题描述】:我正在尝试使用 webpack + traceur 处理 Ecmascript 6 模块以转换为 ES5 CommonJS,但我无法成功对它们进行单元测试。
我尝试使用 Jest + traceur 预处理器,但 automocking 和依赖项名称似乎变得古怪,而且我似乎无法让 sourceMaps 与 Jest 和节点检查器调试一起工作。
是否有更好的框架来对 ES6 模块进行单元测试?
【问题讨论】:
我不知道 Traceur,但 6to5 很适合 jest 并且有一个 6to5-jest plugin。 谢谢詹姆斯,我会去看看。它看起来确实像 6to5 转换成更易读的并且涵盖了我需要的所有 ES6 功能,所以也许我会走这条路。 另外,你能用 node-inspector 调试得到 sourceMaps 吗? 我还推荐使用 Babel.js 将你的 ES6 代码转换成 ES5。在编译测试或实现代码时,我没有遇到任何问题。 babeljs.io 如果您使用的是 babel,那么您也可以简单地使用 babel-plugin-rewire 模拟模块,而不是使用专用的 webpack 或 browserify 加载器。 【参考方案1】:我已经开始在我的测试中使用import * as obj
样式,它将模块中的所有导出作为对象的属性导入,然后可以对其进行模拟。我发现这比使用 rewire 或 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
);
【讨论】:
这种模式或技术应该被更好地记录为模拟 es6 模块而不是依赖第三方依赖项的有用方法。我很想看到随着 es6 在单元测试方面变得更加主流,这种模式在导入模块的示例中更常见,而不是使用import someExport from module
表示法。
既然你提到了 redux 动作,值得指出的是,当动作绑定到连接的组件时,必须密切注意。将间谍应用到已经绑定的函数可能没有效果。了解调用 react-redux 的 connect() 函数的不同方法是我在不失去理智的情况下让它工作的关键:github.com/reactjs/react-redux/blob/master/docs/…
如何防止测试相互渗透?根据我的经验,模拟导入会导致以后的测试使用模拟而不是真实实例,反之亦然。
@user831865 根据您的测试框架,模拟的范围应该是模拟定义的describe
块。如果不了解您的特定设置,很难说。
@user831865 如果你正在使用 sinon,sinon.sandbox
可以防止你的模拟流到你的其他测试中。【参考方案2】:
如果您使用 Webpack,另一个比 rewire 更灵活的选项是 inject-loader。
例如,在与 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()
)
)
inject-loader,以与 proxyquire 类似的方式,至少允许在导入之前注入依赖项,而在 rewire 中,您必须先导入然后 rewire,这使得模拟某些组件(例如,那些具有某些初始化的组件)变得不可能。
【讨论】:
是的,但这仅在您使用require
而不是import .. as ..
syntax 时有效
不正确。我成功地将它与 ES6 导入语法一起使用。我想这取决于您的设置,但我将 Babel 与 babel-loader 一起使用。它适用于 require
和 ES6 导入。唯一需要 require 的地方是在测试本身中模拟依赖项。
嗨@djskinner。我正在尝试让注入加载器与 babel、webpack 和 karam 一起使用,但遇到了麻烦。您介意将您的karma.config.js
(和webpack.config
文件,如果您使用的话)添加到您的答案中。提前致谢。
查看this answer 以获得良好的起点。它主要是关于代码覆盖率,但即使您对代码覆盖率部分不感兴趣,基本配置也很好。至于inject-loader,我不需要任何额外的配置,只需npm i inject-loader
和require(inject!...)
如何将其与 amd 模块一起使用?我得到“模块名称“inject!src/qux_unnormalized2”尚未加载上下文”【参考方案3】:
您好,您可以使用 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);
);
);
【讨论】:
【参考方案4】:我实际上是通过放弃 Jest 并使用 Karma + Jasmine + Webpack 并使用 https://github.com/jhnns/rewire 来模拟依赖项来实现这一点
【讨论】:
函数内部的变量不能通过rewire来改变。这受 javascript 限制,无法通过重新布线来规避。 我很高兴这个建议对你有用......但它是如何被接受的答案? “不要使用那个”不一定有帮助:(【参考方案5】:Proxyquire 会帮助你,但它不适用于现代 webpack+ES6 模块,即“别名”。
import fs from 'fs';
import reducers from 'core/reducers';
...
proxyquire('../module1',
'fs': mockFs, // this gonna work
'core/reducers': mockReducers // what about this?
);
that
将不起作用。
只要你可以模拟 fs - 你就不能模拟减速器。
在任何 webpack 或 babel 转换之后,您必须指定依赖项的 real
名称。通常 - 相对于 module1 位置的名称。可能是“../../../shared/core/reducers”。可能不是。
有一些解决方案 - https://github.com/theKashey/proxyquire-webpack-alias(稳定,基于 proxyquire 的分支)或 https://github.com/theKashey/resolveQuire(不太稳定,可以在原始 proxyquire 上运行)
它们都可以正常工作,并且会以代理查询方式模拟任何 ES6 模块(它们非常好)(这是一个好方法)
【讨论】:
以上是关于如何使用 ES6 模块模拟单元测试的依赖关系的主要内容,如果未能解决你的问题,请参考以下文章
Jest Manual Mocks with React 和 Typescript:模拟 ES6 类依赖