如何解决使用 mocha 和 sinon 的方法装饰器对函数进行单元测试
export class MyClass {
@myDecorator()
public async createItem(itemId: string,itemOptions: ItemOption[]): Promise<boolean> {
// ...
// return await create Item
}
}
export function myDecorator() {
return function check(target: any,propertyKey: string,descriptor: PropertyDescriptor) {
const newDescriptor = Object.assign({},descriptor);
// do something ...
return newDescriptor;
}
}
现在我想对 createItem
方法进行单元测试,而不是对装饰器进行单元测试,而只是对方法进行单元测试。但是由于装饰器的功能有点繁重(其中甚至有一个数据库访问),我需要模拟它,以便它不会在单元测试中被执行。
我在单元测试中使用了 mocha 和 sinon,并且已经尝试了以下方法来模拟装饰器:
const decorator = require('./decorator/MyDecorator');
describe('test createItem method',function () {
it('should return valid result',async function () {
// 1. try
decorator.myDecorator = () => sinon.fake();
// 2. try
const decoratorStub = sinon.stub(decorator,'myDecorator');
decoratorStub.return(true);
// rest of test
const myClass = new MyClass();
let result = await myClass.createItem(itemId,options);
expect(result).to.be.equal(true);
}
}
两次尝试都没有成功。无论如何,装饰器已被执行,但我实际上只想跳过它。
我怎样才能做到这一点?
解决方法
您需要使用额外的包 proxyquire 来存根从包导入的独立函数。我们可以使用这个包来存根 myDecorator
工厂函数。
例如
index.ts
:
import { myDecorator } from './decorator/MyDecorator';
interface ItemOption {}
export class MyClass {
@myDecorator()
public async createItem(itemId: string,itemOptions: ItemOption[]): Promise<boolean> {
return true;
}
}
./decorator/MyDecorator.ts
:
export function myDecorator() {
return function check(target: any,propertyKey: string,descriptor: PropertyDescriptor) {
const newDescriptor = Object.assign({},descriptor);
console.log('real implementation,do heavy and side-effect work');
return newDescriptor;
};
}
index.test.ts
:
import proxyquire from 'proxyquire';
import sinon from 'sinon';
describe('67413500',() => {
it('should pass',async () => {
const myDecoratorStub = sinon.stub().callsFake(() => {
return function check(target: any,descriptor: PropertyDescriptor) {
const newDescriptor = Object.assign({},descriptor);
console.log('fake implementation,skip heavy and side-effect work');
return newDescriptor;
};
});
const { MyClass } = proxyquire('./',{
'./decorator/MyDecorator': {
myDecorator: myDecoratorStub,},});
const cls = new MyClass();
const actual = await cls.createItem();
sinon.assert.match(actual,true);
});
});
单元测试结果:
67413500
fake implementation,skip heavy and side-effect work
✓ should pass (1637ms)
1 passing (2s)
--------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------|---------|----------|---------|---------|-------------------
All files | 55.56 | 100 | 33.33 | 55.56 |
67413500 | 100 | 100 | 100 | 100 |
index.ts | 100 | 100 | 100 | 100 |
67413500/decorator | 20 | 100 | 0 | 20 |
MyDecorator.ts | 20 | 100 | 0 | 20 | 2-5
--------------------|---------|----------|---------|---------|-------------------
,
我回答晚了 1 小时。 X)
但我有使用另一个包的替代解决方案:rewire。
我将 console.log 添加到装饰器以显示假装饰器运行。
// File: decorator.ts
export function myDecorator() {
return function check(target: any,descriptor);
const method = newDescriptor.value;
newDescriptor.value = () => {
console.log('Real Decorator Run');
return method.apply(this);
}
return newDescriptor;
}
}
这是简化的 MyClass。
// File: MyClass.ts
import { myDecorator } from './decorator';
export class MyClass {
@myDecorator()
public async createItem(): Promise<boolean> {
console.log('createItem Called');
return true;
}
}
这是测试文件。
// File: test.ts
import sinon from 'sinon';
import { expect } from 'chai';
import rewire from 'rewire';
import * as decorator from './decorator';
describe('test createItem method',function () {
it('should return valid result',async function () {
// 1. Stub myDecorator.
const decoratorStub = sinon.stub(decorator,'myDecorator');
decoratorStub.returns(function check(a,b,c: PropertyDescriptor) {
const method = c.value;
c.value = () => {
console.log('Fake Decorator');rewire
return method.apply(this);
}
return c;
});
// 2. Rewire MyClass.
const { MyClass } = rewire('./MyClass');
// 3. Test MyClass.
const myClass = new MyClass();
const result = await myClass.createItem();
expect(result).to.be.equal(true);
});
});
当我使用终端运行时:
$ npx nyc ts-mocha test.ts
test createItem method
Fake Decorator
createItem Called
✓ should return valid result (146ms)
1 passing (149ms)
--------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------|---------|----------|---------|---------|-------------------
All files | 52.94 | 100 | 40 | 50 |
MyClass.ts | 100 | 100 | 100 | 100 |
decorator.ts | 11.11 | 100 | 0 | 12.5 | 3-10
--------------|---------|----------|---------|---------|-------------------
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。