我正试图在我的InversifyJS项目上编写单元测试代码。路由测试(使用supertest)工作正常。然后尝试编写sinon stub,spy测试,但不可能成功。下面给出我的示例代码:
DemoRoute
@injectable()
class DemoRoute implements IDemoRoute {
private _demoController:IDemoController;
constructor( @inject(TYPES.IDemoController) demoController:IDemoController ) {
this._demoController = demoController;
}
create(req: Request, res: Response, next: NextFunction) {
return this._demoController.create(req.body)
}
}Demo.test.ts
import "reflect-metadata";
import * as sinon from "sinon";
const sandbox = sinon.createSandbox();
describe.only("Demo Spec 2", () => {
demoController = container.get<IDemoController>(TYPES.IDemoController);
beforeEach((done) => {
insertStub = sandbox.stub(demoController, 'create');
done();
});
afterEach(() => {
sandbox.restore();
});
it("Should call demo route url", async done => {
demoRoute = container.get<IDemoRoute>(TYPES.IDemoRoute);
const stub = insertStub.returns(Promise.resolve({ body: { name: "test xyz", code: "test abc"} }));
const result = await demoRoute.create({body: demoData.validData}, {send: (params) => params});
expect(stub).to.have.been.called; // throw error
done();
});
}单元测试中的错误
UnhandledPromiseRejectionWarning: AssertionError:期望create至少被调用一次,但从未被调用过
我该如何解决这个问题?提前谢谢。
发布于 2019-12-17 04:50:02
原因是注入到DemoRoute的DemoRoute实例和通过以下语句获得的demoController实例:
const demoController = container.get<IDemoController>(TYPES.IDemoController);它们是两个不同的demoController实例。您刚刚在测试文件中为demoController实例创建了一个存根。
在了解了这一点之后,我们可以为DemoController.prototype.create创建一个存根,它将应用于所有实例。
例如。
demoRoute.ts
import "reflect-metadata";
import { IDemoRoute, IDemoController } from "./interfaces";
import { NextFunction } from "express";
import { TYPES } from "./types";
import { inject, injectable } from "inversify";
@injectable()
export class DemoRoute implements IDemoRoute {
private _demoController: IDemoController;
constructor(@inject(TYPES.IDemoController) demoController: IDemoController) {
this._demoController = demoController;
}
create(req: Request, res: Response, next: NextFunction) {
return this._demoController.create(req.body);
}
}demo.test.ts
import "reflect-metadata";
import sinon from "sinon";
import { TYPES } from "./types";
import { IDemoController, IDemoRoute } from "./interfaces";
import { container } from "./inversify.config";
import { DemoController } from "./demoController";
import { expect } from "chai";
const sandbox = sinon.createSandbox();
describe.only("Demo Spec 2", () => {
const demoController = container.get<IDemoController>(TYPES.IDemoController);
let insertStub: sinon.SinonStub<any, any>;
beforeEach(() => {
insertStub = sandbox.stub(DemoController.prototype, "create");
});
afterEach(() => {
sandbox.restore();
});
it("Should call demo route url", async () => {
const demoData = { validData: {} };
const demoRoute = container.get<IDemoRoute>(TYPES.IDemoRoute);
// different demoController instances
expect(demoRoute["_demoController"]).not.to.be.equal(demoController);
insertStub.returns(Promise.resolve({ body: { name: "test xyz", code: "test abc" } }));
const nextStub = sinon.stub();
const result = await demoRoute.create(
{ body: demoData.validData } as any,
{ send: (params) => params } as any,
nextStub,
);
sinon.assert.calledOnce(insertStub);
});
});包含覆盖率报告的单元测试结果:
Demo Spec 2
✓ Should call demo route url
1 passing (25ms)
---------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------|----------|----------|----------|----------|-------------------|
All files | 96.08 | 100 | 80 | 95.56 | |
demo.test.ts | 95.83 | 100 | 80 | 95.45 | 31 |
demoController.ts | 85.71 | 100 | 50 | 80 | 7 |
demoRoute.ts | 100 | 100 | 100 | 100 | |
inversify.config.ts | 100 | 100 | 100 | 100 | |
types.ts | 100 | 100 | 100 | 100 | |
---------------------|----------|----------|----------|----------|-------------------|完整的示例源代码:https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/59103140
发布于 2020-04-01 18:29:45
我们能够通过从反版本容器中提取实例的方法,再加上使用sinon.createStubInstance()来模拟构造函数来实现顽固性。看起来是这样的:
describe.only("Demo Spec 2", () => {
demoController = container.get<IDemoController>(TYPES.IDemoController);
demoControllerStub = sinon.createStubInstance(IDemoController) as SinonStubbedInstance<IDemoController>;
beforeEach((done) => {
insertStub = sinon.stub(demoController, 'create');
done();
});
afterEach(() => {
sandbox.restore();
});
it("Should call demo route url", async done => {
demoRoute = container.get<IDemoRoute>(TYPES.IDemoRoute);
const stub = insertStub.returns(Promise.resolve({ body: { name: "test xyz", code: "test abc"} }));
const result = await demoRoute.create({body: demoData.validData}, {send: (params) => params});
expect(demoControllerStub.create.calledOnce);
done();
});
}为我们工作的另一种方式,我更喜欢,是存根实例,并取消绑定和绑定的存根实例到容器。看起来是这样的:
describe.only("Demo Spec 2", () => {
demoControllerStub = sinon.createStubInstance(IDemoController);
container.unbind(IDemoController);
container.bind(IDemoController).toConstantValue(demoControllerStub);
beforeEach((done) => {
demoControllerStub.create.reset();
done();
});
it("Should call demo route url", async done => {
demoRoute = container.get<IDemoRoute>(TYPES.IDemoRoute);
const stub = insertStub.returns(Promise.resolve({ body: { name: "test xyz", code: "test abc"} }));
const result = await demoRoute.create({body: demoData.validData}, {send: (params) => params});
sinon.assert.calledWith(demoControllerStub.create, sinon.match({body: demoData.validData});
done();
});
}https://stackoverflow.com/questions/59103140
复制相似问题