- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我希望能够正确测试我的 ES6 类,它的构造函数需要另一个类,所有这些看起来像这样:
A 级
class A {
constructor(b) {
this.b = b;
}
doSomething(id) {
return new Promise( (resolve, reject) => {
this.b.doOther()
.then( () => {
// various things that will resolve or reject
});
});
}
}
module.exports = A;
B 级
class B {
constructor() {}
doOther() {
return new Promise( (resolve, reject) => {
// various things that will resolve or reject
});
}
module.exports = new B();
索引
const A = require('A');
const b = require('b');
const a = new A(b);
a.doSomething(123)
.then(() => {
// things
});
由于我尝试进行依赖注入(inject)而不是在类的顶部要求,我不确定如何模拟类 B 及其用于测试类 A 的函数。
最佳答案
Sinon 允许您轻松地 stub 对象的各个实例方法。当然,由于 b
是单例,您需要在每次测试后回滚它,以及您可能对 b
所做的任何其他更改。如果不这样做,调用计数和其他状态将从一个测试泄漏到另一个测试。如果这种全局状态处理不当,您的套件可能会变成一团乱七八糟的测试,这取决于其他测试。
重新安排一些测试?以前没有的东西失败了。添加、更改或删除测试?一堆其他测试现在失败了。尝试运行单个测试或测试子集?他们现在可能会失败。或者更糟的是,当您编写或编辑它们时,它们单独通过,但在整个套件运行时失败。
相信我,这很糟糕。
因此,按照此建议,您的测试可能如下所示:
const sinon = require('sinon');
const { expect } = require('chai');
const A = require('./a');
const b = require('./b');
describe('A', function() {
describe('#doSomething', function() {
beforeEach(function() {
sinon.stub(b, 'doSomething').resolves();
});
afterEach(function() {
b.doSomething.restore();
});
it('does something', function() {
let a = new A(b);
return a.doSomething()
.then(() => {
sinon.assert.calledOnce(b.doSomething);
// Whatever other assertions you might want...
});
});
});
});
但是,这并不是我所推荐的。
我通常尽量避免教条式的建议,但这是少数异常(exception)之一。如果您正在进行单元测试、TDD 或 BDD,通常应该避免使用单例。它们不能与这些实践很好地结合,因为它们使测试后的清理变得更加困难。在上面的示例中,它非常简单,但是随着 B
类添加了越来越多的功能,清理工作变得越来越繁重并且容易出错。
那么你会怎么做呢?让您的 B
模块导出 B
类。如果你想保留你的 DI 模式并避免在 A
模块中需要 B
模块,你只需要创建一个新的 B
每次创建一个 A
实例时实例。
按照这个建议,您的测试可能看起来像这样:
const sinon = require('sinon');
const { expect } = require('chai');
const A = require('./a');
const B = require('./b');
describe('A', function() {
describe('#doSomething', function() {
it('does something', function() {
let b = new B();
let a = new A(b);
sinon.stub(b, 'doSomething').resolves();
return a.doSomething()
.then(() => {
sinon.assert.calledOnce(b.doSomething);
// Whatever other assertions you might want...
});
});
});
});
您会注意到,因为每次都会重新创建 B
实例,所以不再需要恢复 stub 的 doSomething
方法。
Sinon 还有一个简洁的实用函数,叫做 createStubInstance这使您可以避免在测试期间完全调用 B
构造函数。它基本上只是为任何原型(prototype)方法创建一个带有 stub 的空对象:
const sinon = require('sinon');
const { expect } = require('chai');
const A = require('./a');
const B = require('./b');
describe('A', function() {
describe('#doSomething', function() {
it('does something', function() {
let b = sinon.createStubInstance(B);
let a = new A(b);
b.doSomething.resolves();
return a.doSomething()
.then(() => {
sinon.assert.calledOnce(b.doSomething);
// Whatever other assertions you might want...
});
});
});
});
最后,与问题没有直接关系的最后一点建议 -- Promise
构造函数不应该用于包装 promises。这样做是多余且令人困惑的,并且违背了使异步代码更易于编写的 promise 的目的。
Promise.prototype.then方法带有内置的有用行为,因此您永远不必执行这种冗余包装。调用 then
总是返回一个 promise(我将在下文中称之为“链式 promise”),其状态将取决于处理程序:
then
处理程序将导致链式 promise 以该值解析。then
处理程序将导致链式 promise 拒绝抛出的值。then
处理程序将使链接的 promise 与返回的 promise 的状态相匹配。因此,如果它使用一个值解决或拒绝,则链式 promise 将使用相同的值解决或拒绝。所以你的 A
类可以像这样大大简化:
class A {
constructor(b) {
this.b = b;
}
doSomething(id) {
return this.b.doOther()
.then(() =>{
// various things that will return or throw
});
}
}
module.exports = A;
关于javascript - 如何使用 sinon 正确模拟 ES6 类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50389981/
我有一个正在 stub 的函数,该函数使用多个参数进行调用。我想检查仅第一个参数。其余的都是回调函数,所以我不想管它们。因此,我可能有以下 2 个调用,以 ajax 为例: method.get =
我试图用 Sinon.js 拦截一个调用,这样我就可以做一些日志记录,然后执行原始调用。我看不到用 sinon.spy() 做到这一点的方法,但我想我可以用 sinon.stub() 做到这一点。 我
我是SinonJs的初学者,当我尝试编写一些演示代码时,它无法工作,我不知道为什么。 应用程序.js const db = require('./db'); module.exports.si
sinon-mongoose = 2.3.0不适用于 sinon 9我的代码如下 // test.js const sinon = require('sinon'); require('sinon-m
我正在尝试创建一个单元测试来测试我的代码创建的对象是否已成功发送到更新函数(当前不测试更新函数,因为这将是集成测试)。 我想做的是使用 sinon 模拟来检查更新方法的输入是否正确: var obje
当使用 sinon 时,我只想替换函数的返回值,不需要其他信息,例如调用了多少次。哪一个更好? sinon.replace(Component.prototype, 'getValue', () =>
我在获取一个 sinon stub 来返回/解析另一个 sinon stub 时遇到问题。我正在使用 sinon、chai、chai-as-promised 和 mocha。 我正在按顺序执行多个异步
在我们的前端单元测试中使用 sinon 和 sinon-qunit,我很难理解这些方法的区别。我们正在使用 sinon.sandbox.stub()(字面意思是函数,我们不创建沙箱)并且这些 stub
我正在尝试调用 fail我的片段中的条件。 但是当我使用 sinon.stub().throws()方法它显示我错误。 我无法在代码中处理它。 这是我的片段: login() { let lo
我有一个中间件功能,它检查 session token 以查看用户是否是管理员用户。如果所有检查都通过,该函数不会返回任何内容,而只是调用 next()。 在对 Sinon spy 的 next()
我想在node.js和mocha上使用sinon。我在测试文件中执行以下操作: var sinon = require('sinon') 之后 sinon var 是空对象。 global.sinon
在下面的单元测试代码中: TestModel = Backbone.Model.extend({ defaults: { 'selection': null },
当我在对象内部的函数上使用 Sinon 时,它可以工作: function myFunc() { console.log('hello'); } var myObj = { myFunc: m
假设我想通过添加我自己的特殊 stub 来扩展 sinon:我可以这样写: sinon.specialStub = function() { return this.stub().return
我有一个函数需要使用 sinon 进行测试。它需要两个参数并具有可以引发的不同事件。我正在尝试模拟“准备就绪”事件,以模拟成功的 SFTP 连接 function configureSFTPConne
我有一个非常简单的 JS 库(称为 trysinon.js),如下所示: export function foo() { bar(); } export function bar() { ret
我们正在使用Sinon.JS对于一些集成测试,我遇到了一个问题,我需要以某种方式进行调试。我们使用 fakeServer.create() 方法,如下所示: server = Sinon.fakeSe
我发现了几个相关的问题,但似乎没有一个对我想要实现的内容有帮助。 因此,我想监视构造函数方法,以便当使用构造函数创建的对象在不同范围另一个函数中调用此方法时,我可以知道该调用所使用的参数制作。 示例:
我正在尝试为这个简单的中间件功能编写一个独立的测试 function onlyInternal (req, res, next) { if (!ReqHelpers.isInternal(req)
我觉得我大致了解 sinon 的工作原理,但在监视传递给我正在测试的导入函数的回调时遇到了一些麻烦。这是一个例子: import sinon from 'sinon' const callbackCa
我是一名优秀的程序员,十分优秀!