Typescript+自定义Inject+Jasmine/Karma(单元测试及覆盖率报告)

有angular、nestjs经验的程序员一定会意识到依赖注入(dependency injection)带来的便利, 当然依赖注入不是什么新鲜的概念,并且也不是nodejs平台首创。
本文旨在介绍如果自己定义一个简单的注入帮助类。

实践步骤如下:
安装依赖
npm init --y
npm install typescript ts-node jasmine jasmine-core @types/jasmine lodash --save-dev
npm install karma karma-chrome-launcher karma-cli karma-coverage karma-jasmine karma-typescript --save

添加配置
tsconfig.json

{
    "compilerOptions": {
      "target": "es5",
      "module": "commonjs",
      "lib": ["es2015", "es2016", "es2017", "dom"],
      "declaration": true,
      "outDir": "./lib",
      "rootDir": "./src/",
      "strictNullChecks": false,
      "noImplicitThis": true,
      "noUnusedParameters": true,
      "noImplicitReturns": true,
      "alwaysStrict": false,
      "noFallthroughCasesInSwitch": true,
      "baseUrl": "./src/",
      "experimentalDecorators": true,
      "emitDecoratorMetadata": true,
      "downlevelIteration": true,
      "strict": false,
      "isolatedModules": true,
      "noUncheckedIndexedAccess": true,
      "esModuleInterop": true
    },
    "exclude": ["site", "lib"]
  }

确保experimentalDecorators,emitDecoratorMetadata都有启用

jasmine.json

{
    "spec_files": ["**/*[sS]pec.ts"]
}

karma.conf.js

module.exports = function (config) {
    config.set({
      basePath: '',
      frameworks: ["jasmine", "karma-typescript"],
      files: [
        './src/unittest-demo/*.ts'
      ],
      exclude: [
        'karma.conf.js'
      ],
      preprocessors: {
        "./src/unittest-demo/*.ts": ["karma-typescript"] // *.tsx for React Jsx
      },
      reporters: ["progress", "karma-typescript"],
      port: 9876,
      colors: true,
      logLevel: config.LOG_INFO,
      autoWatch: true,
      browsers: ['Chrome'],
      singleRun: false,
      concurrency: Infinity
    })
  }

注意./src/unittest-demo/*.ts 这个配置表示要对哪些ts文件产出coverage report

新建folder -> src/unittest-demo, 添加文件app.ts, app.spec.ts, inject.ts

inject.ts

import { find } from 'lodash';

export interface IContainerProvider {
    useValue: any;
    token: string;
}

export class Container {
  providers: { [key: string]: any } = {};

  public resolve(token: string) {
    const matchedProvider = find(
      this.providers,
      (_provider, key) => key === token
    );

    if (matchedProvider) {
      return matchedProvider;
    } else {
      throw new Error(`No provider found for ${token}!`);
    }
  }

  public provide(details: IContainerProvider): void {
    this.providers[details.token] = details.useValue;
  }
}

export const container = new Container();

export function Injectable(token: string): Function {
    return function(target: { new () }): void {
      container.providers[token] = new target();
    };
  }

export function Inject(token: string) {
    return function(target: any, key: string) {
      Object.defineProperty(target, key, {
        get: () => container.resolve(token),
        enumerable: true,
        configurable: true
      });
    };
  }

app.ts

    @Injectable('testService')
    export class TestService {
        public log(msg: string): void {
            console.log(msg);
        }
    }

    @Injectable('consumer')
    export class Consumer {
        @Inject('testService') private testService;

        constructor() {
            this.testService.log('Hey!');
        }
    }

    container.resolve('consumer');

injectable标签会为该class新建一个实例,并加入到container的providers对象中,以供后续使用。
inject标签则是使用该实例,通过defineProperty方法修改对应属性的get方法,返回实例。

最后是单元测试部分
package.json -> scripts加入

    "test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
    "coverage": "karma start karma.conf.js"

app.spec.ts

import * as app from './app-inject';
import { container, Injectable, Inject } from './inject';

class mockService {
    public log(msg: string): void {
        console.log('from mock service');
    }
}

describe('demo', function() {
    container.provide({
        token: 'testService',
        useValue: new mockService()
    })

    it('unit-test', function() {
        let result = app.Consumer;
        expect(result).toBeDefined();
    })
})

上述代码通过替换依赖的方式,使用mockService代替了原先的testService类。

最后npm run coverage, 成功后可以在项目根目录下看到coverage文件夹
Typescript+自定义Inject+Jasmine/Karma(单元测试及覆盖率报告)

进入coverage\Chrome 94.0.4606.61 (Windows 10)\html, 打开index.html就可以看到coverage report
Typescript+自定义Inject+Jasmine/Karma(单元测试及覆盖率报告)

上一篇:typescript 入门


下一篇:阿里云轻量应用服务器教程–如何远程应用服务器(Linux)