我有这个功能:
let removePresentation = function(presentationName, callback) {
let rimraf = require('rimraf');
callback();
callback();
callback();
if(!presentationName || !presentationName.trim()) {
callback();
return;
}
presentationName = presentationName.replace('.zip', '');
rimraf('./presentations/' + presentationName, function(err) {
if(err) {
console.log(err);
}
callback();
});
};
exports.removePresentation = removePresentation;
我试图用以下方法测试它:
var chai = require('chai'),
expect = require('chai').expect,
sinonChai = require('sinon-chai'),
sinon = require('sinon'),
mock = require('mock-require');
chai.use(sinonChai);
describe('removePresentation', function() {
var sandbox;
var callback;
var rimrafSpy;
beforeEach(function() {
sandbox = sinon.sandbox.create();
mock('../business/communications_business', {});
rimrafSpy = sinon.spy();
callback = sinon.spy();
mock('rimraf', rimrafSpy);
});
afterEach(function() {
sandbox.restore();
});
it('should call rimraf if presentation name is valid', function(done) {
let RoomStateBusiness = require('../business/roomstate_business');
RoomStateBusiness.removePresentation('name.zip', callback);
expect(rimrafSpy).to.have.been.calledWith('./presentations/name');
expect(callback).to.have.been.called.once;
done();
});
it('should not call rimraf if presentation name is null', function(done) {
let RoomStateBusiness = require('../business/roomstate_business');
RoomStateBusiness.removePresentation(null, callback);
expect(rimrafSpy).not.to.have.been.called;
expect(callback).to.have.been.called.once;
done();
});
it('should not call rimraf if presentation name is whitespace', function(done) {
let RoomStateBusiness = require('../business/roomstate_business');
RoomStateBusiness.removePresentation(' ', callback);
expect(rimrafSpy).not.to.have.been.called;
expect(callback).to.have.been.called.once;
done();
});
it('should not call rimraf if presentation name is empty string', function(done) {
let RoomStateBusiness = require('../business/roomstate_business');
RoomStateBusiness.removePresentation('', callback);
expect(rimrafSpy).not.to.have.been.called;
expect(callback).to.have.been.called.once;
done();
});
});
即使我清楚地多次调用callback()(仅在测试时),expect(callback).to.have.been.called.once;
总是断言为true。我已经检查了Chai api,它希望调用只有一次,尽管无论我多少次调用callback(),它总是通过。我做错了什么?
答案 0 :(得分:5)
没有expect(fn).to.have.been.called.once
这样的断言。
根据sinon-chai
docs,只有:
expect(fn).to.have.been.called
expect(fn).to.have.been.calledOnce
这是chai
的一个已知问题,为什么只有getter-assertions才是坏事。 Chai允许你编写一段看起来像属性访问的代码(即断言本身并不以函数调用结束)来断言...无论你想断言什么。这使用属性getter来执行必要的代码。
问题在于,如果你输错字或其他错误,表达式将简单地计算为undefined
(你正在访问一个不存在的属性)和没有执行断言代码,导致测试通过(因为如果抛出异常,测试只会失败)。
在您的情况下,called
有一个断言,这很可能会返回一个对象。不幸的是,该对象没有断言once
,因此没有执行代码并且测试通过。
您有两种选择:
Proxy
支持的Chai 4和Node.js版本(不确定添加代理支持的位置,可能是Node.js 5或6) - chai通过代理所有属性访问来介绍防范这些问题的安全措施一个Proxy对象,用于检查您是否使用了有效的断言undefined is not a function
错误而失败在我看来,第二种选择是高度优先的,因为对测试用例的正确性毫无疑问。即使在支持的平台上,仍然可以关闭Chai代理支持。
答案 1 :(得分:0)
假设我们在谈论sinon-chai
,那么它应该是calledOnce
而不是called.once
。如果您执行called
任意数量的通话> 0将通过测试。