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

时间:2020-02-16 12:10:20

标签: node.js typescript unit-testing express mocha

我目前正在为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)

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

这是我的配置文件

package.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"
    }
}

mocha.opts

--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

0 个答案:

没有答案