Ошибка внедрения зависимости Служба тестирования модуля TSED - PullRequest
2 голосов
/ 16 февраля 2020

В настоящее время я пишу тесты для проекта TSED (express), и всякий раз, когда я вызываю службу, используя внедрение зависимостей в один файл, я получаю сообщение об ошибке, что служба не определена в совершенно другой части проекта. Помните, что весь код во всех других не тестовых файлах работает правильно.

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 .spe c .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.spe c .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"
    }
}

мокко. 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...