依赖项注入错误 TSED 单元测试服务



我目前正在为 TSED 项目(快速(编写测试,每当我在一个文件中使用依赖注入调用服务时,我都会收到一个错误,指出服务在项目的完全不同的部分中未定义。请注意,所有其他非测试文件中的所有代码都可以正常工作。

DBConnectorService.ts处理对数据库的所有查询。

import { Service, Scope, ProviderScope } from "@tsed/di";
import { Pool } from "pg";
import { GLOBAL_DB_POOL } from "../server";
@Service()
export class DBConnectorService {
private pool: Pool;
constructor() {
this.pool = GLOBAL_DB_POOL;
}
executeQuery(query) {
return new Promise(resolve => {
this.pool.query(query, (err, results) => {
if (err) {
throw err;
}
resolve(results.rows);
});
});
}
}

DBConnectorService.spec.ts处理上述文件的测试。

import { DBConnectorService } from "./../../../src/services";
import { inject } from '@tsed/testing';
import { expect } from 'chai';
describe('DBConnectorService', () => {
it('should be an instance', () => {
const dbConnectorService = new DBConnectorService();
expect(dbConnectorService).to.be.an.instanceof(DBConnectorService);
});
});

运行此测试时,输出为:

❯ npm run test:unit
DBConnectorService
✓ should be an instance
1 passing (4ms)

这太好了,它应该工作。但是,当我将服务在测试中的实现方式从实例化更改为注入时,如下所示:

import { DBConnectorService } from "./../../../src/services";
import { inject } from '@tsed/testing';
import { expect } from 'chai';
describe('DBConnectorService', () => {
it('should be an instance', inject([DBConnectorService], (dbConnectorService: DBConnectorService) => {
expect(dbConnectorService).to.be.an.instanceof(DBConnectorService);
}));
});

并再次运行测试,我收到以下错误:

❯ npm run test:unit
1 failing (31ms)
1) DBConnectorService
should be an instance:
INJECTION_ERROR: Injection failed on FormatCtrl
Origin: Unable to inject dependency. Given token is undefined. Have you enabled emitDecoratorMetadata in your tsconfig.json or decorated your class with @Injectable, @Service, ... decorator ?
FormatCtrl->constructor(formatService: undefined)
^‾‾‾‾‾‾‾‾‾‾‾‾
at Function.throwInjectorError (node_modules/@tsed/di/src/errors/InjectionError.ts:43:11)
at Map.resolve (node_modules/@tsed/di/src/services/InjectorService.ts:526:22)
at Map.invoke (node_modules/@tsed/di/src/services/InjectorService.ts:194:38)
at Map.loadSync (node_modules/@tsed/di/src/services/InjectorService.ts:249:16)
at Map.<anonymous> (node_modules/@tsed/di/src/services/InjectorService.ts:277:19)
at Generator.next (<anonymous>)
at fulfilled (node_modules/tslib/tslib.js:107:62)

这完全没有意义,因为文件FormatCtrl.ts

import { Controller, Get } from "@tsed/common";
import { FormatService } from "../services";
@Controller('/formats')
export class FormatCtrl {
constructor(private formatService: FormatService) { }
@Get('/')
findAll() {
return new Promise(resolve => {
this.formatService.findAll().then(resolve);
});
}
}

使用FormatService.ts

import { Service } from '@tsed/di';
import { DBConnectorService } from './DBConnectorService';
@Service()
export class FormatService {
constructor(private dbConnectorService: DBConnectorService) { }
findAll() {
return new Promise(resolve => {
this.dbConnectorService.executeQuery('SELECT * FROM formats').then(resolve);
});
}
}

它只是使用DBConnectorService

更奇怪的是,如果我两次定义完全相同的测试,就会发生这种情况:

DBConnectorService.spec.ts

import { DBConnectorService } from "./../../../src/services";
import { inject } from '@tsed/testing';
import { expect } from 'chai';
describe('DBConnectorService', () => {
it('should be an instance 1', inject([DBConnectorService], (dbConnectorService: DBConnectorService) => {
expect(dbConnectorService).to.be.an.instanceof(DBConnectorService);
}));
it('should be an instance 2', inject([DBConnectorService], (dbConnectorService: DBConnectorService) => {
expect(dbConnectorService).to.be.an.instanceof(DBConnectorService);
}));
});

输出:

❯ npm run test:unit
DBConnectorService
1) should be an instance 1
✓ should be an instance 2
1 passing (32ms)
1 failing
1) DBConnectorService
should be an instance 1:
INJECTION_ERROR: Injection failed on FormatCtrl
Origin: Unable to inject dependency. Given token is undefined. Have you enabled emitDecoratorMetadata in your tsconfig.json or decorated your class with @Injectable, @Service, ... decorator ?
FormatCtrl->constructor(formatService: undefined)
^‾‾‾‾‾‾‾‾‾‾‾‾
at Function.throwInjectorError (node_modules/@tsed/di/src/errors/InjectionError.ts:43:11)
at Map.resolve (node_modules/@tsed/di/src/services/InjectorService.ts:526:22)
at Map.invoke (node_modules/@tsed/di/src/services/InjectorService.ts:194:38)
at Map.loadSync (node_modules/@tsed/di/src/services/InjectorService.ts:249:16)
at Map.<anonymous> (node_modules/@tsed/di/src/services/InjectorService.ts:277:19)
at Generator.next (<anonymous>)
at fulfilled (node_modules/tslib/tslib.js:107:62)

因此,当我第一次尝试使用注入时,它失败了,但是之后如果我在同一文件中再次使用它,它会神奇地工作。即使我第三次实施完全相同的测试,它也能工作。有人知道这里发生了什么吗?

这是我的配置文件

包.json

{
"name": "back-end",
"version": "1.0.0",
"description": "",
"main": "src/index.js",
"scripts": {
"clean": "rimraf '{src,test}/**/*.{js,js.map}'",
"build": "yarn tsc",
"test": "yarn clean && yarn test:lint && yarn test:coverage",
"test:unit": "cross-env NODE_ENV=test mocha",
"test:coverage": "cross-env NODE_ENV=test nyc mocha",
"test:lint": "tslint --project tsconfig.json",
"test:lint:fix": "tslint --project tsconfig.json --fix",
"travis:deploy-once": "travis-deploy-once",
"travis:coveralls": "nyc report --reporter=text-lcov | coveralls",
"tsc": "tsc --project tsconfig.json",
"tsc:w": "tsc --project tsconfig.json -w",
"start": "nodemon --watch "src/**/*.ts" --ignore "node_modules/**/*" --exec ts-node src/index.ts",
"start:prod": "cross-env NODE_ENV=production node dist/index.js",
"docker:build": "yarn build && docker-compose build",
"deploy": "exit 0"
},
"author": "",
"license": "ISC",
"dependencies": {
"@tsed/common": "5.42.1",
"@tsed/core": "5.42.1",
"@tsed/di": "5.42.1",
"@tsed/multipartfiles": "^5.42.1",
"@tsed/swagger": "5.42.1",
"@tsed/testing": "5.42.1",
"body-parser": "1.19.0",
"compression": "1.7.4",
"concurrently": "5.0.0",
"cookie-parser": "1.4.4",
"cors": "2.8.5",
"cross-env": "6.0.3",
"express": "4.17.1",
"fluent-ffmpeg": "^2.1.2",
"jimp": "^0.9.3",
"method-override": "^3.0.0",
"node-uuid": "1.4.8",
"pg": "^7.18.1"
},
"devDependencies": {
"@types/chai": "4.2.5",
"@types/chai-as-promised": "7.1.2",
"@types/cors": "2.8.6",
"@types/express": "4.17.2",
"@types/fluent-ffmpeg": "^2.1.14",
"@types/http-proxy": "1.17.2",
"@types/mocha": "5.2.7",
"@types/multer": "^1.4.2",
"@types/node": "12.12.9",
"@types/pg": "^7.14.1",
"@types/request-promise": "4.1.45",
"@types/sinon": "7.5.0",
"@types/sinon-chai": "3.2.3",
"@types/supertest": "2.0.8",
"@types/swagger-schema-official": "^2.0.20",
"@types/uuid": "^3.4.7",
"chai": "4.2.0",
"chai-as-promised": "7.1.1",
"concurrently": "5.0.0",
"mocha": "6.2.2",
"nodemon": "1.19.4",
"nyc": "14.1.1",
"rimraf": "3.0.0",
"sinon": "7.5.0",
"sinon-chai": "3.3.0",
"supertest": "4.0.2",
"ts-node": "^8.6.2",
"tsconfig-paths": "^3.9.0",
"tslint": "5.20.1",
"typescript": "3.7.5"
}
}

摩卡·

--require node_modules/ts-node/register
{src,test}/**/*.spec.ts

tsconfig.json

{
"compilerOptions": {
"baseUrl": ".",
"outDir": "./dist",
"target": "ES2016",
"lib": [
"ES2016",
"DOM"
],
"typeRoots": [
"./node_modules/@types"
],
"module": "CommonJS",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"sourceMap": true,
"declaration": false,
"allowSyntheticDefaultImports": true,
"noImplicitAny": false,
"isolatedModules": false
},
"include": [
"./src/**/*.ts"
],
"exclude": [
"./public",
"dist"
]
}

同样值得注意的是,我按照以下步骤创建了该项目:https://tsed.io/getting-started.html

对于第一个测试:

const dbConnectorService = new DBConnectorService();
expect(dbConnectorService).to.be.an.instanceof(DBConnectorService);

它之所以有效,是因为您创建了一个新实例。

第二个

it('should be an instance', inject([DBConnectorService], (dbConnectorService: DBConnectorService) => {
expect(dbConnectorService).to.be.an.instanceof(DBConnectorService);
}));

它失败,因为您尝试注入服务,但没有实例化任何内容

溶液

您应该使用beforeEach(PlatformTest.bootstrap(Server));beforeEach(PlatformTest.create);此代码将创建一个新上下文并注入所有服务。

然后,您可以通过两个命令使用您的服务:

  • const service = PlatformTest.get<MyService>(MyService);
  • beforeEach(PlatformTest.create);
    ... // Inside your test
    const locals = [
    {
    token: DbService,
    use: {
    getData: () => {
    return "test";
    }
    }
    }
    ];
    const instance: MyService= await PlatformTest.invoke(MyService, locals);```
    

第一个获取注入平台的服务。 第二个通过模拟 DI 获得服务。

有关详细信息,请参阅文档。

在您的情况下

代码应该是这样的:

import { DBConnectorService } from "./../../../src/services";
import { inject } from '@tsed/testing';
import { expect } from 'chai';

describe('DBConnectorService', () => {
beforeEach(PlatformTest.create);
it('should be an instance', async () => {
const dbConnectorService= await PlatformTest.get<DBConnectorService>(DBConnectorService);
expect(dbConnectorService).to.be.an.instanceof(DBConnectorService);
}));
});

希望对你有帮助

最新更新