鉴于(过度简化)片段:
import Validator from 'validator';
export default function isValid(arg) {
// Validator#isValid is an ES6 getter
return new Validator(arg).isValid;
}
如何测试使用给定参数实例化Validator
?和存根isValid
?
我知道我可以重构我的代码以避免这个问题,我不是在寻找一个解决方法,因为我发现很多(依赖注入,不使用ES6糖等)。
我找到了一种方法,但它非常丑陋。在测试文件中:
import ValidatorNamespace from 'validator';
const Validator = ValidatorNamespace.default;
let validatorInstance;
let validatorConstructor;
const subject = arg => isValid(arg);
const validityStatus = true;
describe('isValid helper', () => {
beforeEach(() => {
validatorInstance = sinon.createStubInstance(Validator);
// Yay! This is how I managed to spy on the constructor!.. :(
validatorConstructor = sandbox.stub(ValidatorNamespace, 'default').
returns(validatorInstance);
sandbox.stub(validatorInstance, 'isValid').value(validityStatus);
});
it('instantiates the validator properly', ()=> {
subject('arg');
expect(validatorConstructor).to.have.been.calledWith('arg')
});
it('returns the value returned by the validator', ()=> {
expect(subject('arg')).to.eq(validityStatus);
});
});
验证码:
export default class Validator {
constructor(arg) {
this.innerValue = arg;
}
get isValid() {
return aFunctionOf(this.innerValue);
}
}
答案 0 :(得分:2)
你想要的是不可能的。 Stubbing需要某种"接缝"通过它将存根放在适当的位置。当您在生产代码中直接导入函数(构造函数或其他函数)时,您唯一要离开的接缝就是导入过程本身。
有proxyquire,它会覆盖节点中的require
次调用。我不知道你正在使用什么环境,而且我真的不知道这对ES6模块有多好。但是,如果您正在使用babel转发到ES6,它应该可以正常工作。
根据我的经验,这种东西不值得额外的复杂性。我通常的解决方法是只创建一个静态工厂函数和stub / use,而不是直接使用构造函数:
export default class Validator {
constructor(arg) {
this.innerValue = arg;
}
static create(arg) {
return new Validator(arg);
}
get isValid() {
return aFunctionOf(this.innerValue);
}
}
如果你想为工厂进行单元测试,你可以简单地检查返回的实例,而不是截断构造函数:
it('create should return an instance', function() {
let arg = { foo: 'bar' };
let result = Validator.create(arg);
expect(result).to.be.an.instanceof(Validator);
expect(result.innerValue).to.equal(arg);
});