TypeScript中的依赖注入

时间:2013-04-17 20:15:40

标签: tdd requirejs typescript amd

我正在研究使用TypeScript进行TDD的可能性。 如果我在TypeScript中编写我的测试,是否可以使import语句为我测试的类返回模拟? 或者是用纯javascript编写测试并处理自己注入AMD的唯一​​可行方法吗?

9 个答案:

答案 0 :(得分:20)

我在TypeScript中使用infuse.js进行依赖注入。

参考d.ts

/// <reference path="definition/infusejs/infusejs.d.ts"/>

在启动时初始化进样器

this.injector = new infuse.Injector();  

地图依赖

this.injector.mapClass( 'TodoController', TodoController );
this.injector.mapClass( 'TodoView', TodoView );
this.injector.mapClass( 'TodoModel', TodoModel, true );  // 'true' Map as singleton

注入依赖关系

export class TodoController
{
    static inject = ['TodoView', 'TodoModel'];

    constructor( todoView:TodoView, todoModel:TodoModel )
    {

    }
 }

它是基于字符串的,而不是基于类型的(因为TypeScript中的反射是不可能的)。尽管如此,它在我的应用程序中运行良好。

答案 1 :(得分:18)

我开发了一个名为InversifyJS的IoC容器,它具有高级依赖注入功能,如上下文绑定。

您需要按照 3个基本步骤来使用它:

1。添加注释

注释API基于Angular 2.0:

prod

2。声明绑定

绑定API基于Ninject:

import { injectable, inject } from "inversify";

@injectable()
class Katana implements IKatana {
    public hit() {
        return "cut!";
    }
}

@injectable()
class Shuriken implements IShuriken {
    public throw() {
        return "hit!";
    }
}

@injectable()
class Ninja implements INinja {

    private _katana: IKatana;
    private _shuriken: IShuriken;

    public constructor(
        @inject("IKatana") katana: IKatana,
        @inject("IShuriken") shuriken: IShuriken
    ) {
        this._katana = katana;
        this._shuriken = shuriken;
    }

    public fight() { return this._katana.hit(); };
    public sneak() { return this._shuriken.throw(); };

}

3。解决依赖关系

决议API基于Ninject:

import { Kernel } from "inversify";

import { Ninja } from "./entities/ninja";
import { Katana } from "./entities/katana";
import { Shuriken} from "./entities/shuriken";

var kernel = new Kernel();
kernel.bind<INinja>("INinja").to(Ninja);
kernel.bind<IKatana>("IKatana").to(Katana);
kernel.bind<IShuriken>("IShuriken").to(Shuriken);

export default kernel;

最新版本(2.0.0)支持许多用例:

  • 内核模块
  • 内核中间件
  • 使用类,字符串文字或符号作为依赖标识符
  • 注入常数值
  • 注入类构造函数
  • 注入工厂
  • 自动工厂
  • 注入提供者(异步工厂)
  • 激活处理程序(用于注入代理)
  • 多次进样
  • 标记绑定
  • 自定义标签装饰器
  • 命名绑定
  • 上下文绑定
  • 友好例外(例如循环依赖)

您可以在https://github.com/inversify/InversifyJS

了解有关此内容的更多信息

答案 2 :(得分:6)

试试这个Dependency Injector (Typejector)

GitHub Typejector

使用新的TypeScript 1.5可以使用注释方式

例如

    @injection
    class SingletonClass {
        public cat: string = "Kitty";
        public dog: string = "Hot";

        public say() {
            alert(`${this.cat}-Cat and ${this.dog}-Dog`);
        }
    }
    @injection
    class SimpleClass {
        public say(something: string) {
            alert(`You said ${something}?`);
        }
    }

    @resolve
    class NeedInjectionsClass {
        @inject(SingletonClass)
        public helper: SingletonClass;
        @inject(SimpleClass)
        public simpleHelper: SimpleClass;

        constructor() {
            this.helper.say();
            this.simpleHelper.say("wow");
        }
    }
    class ChildClass extends NeedInjectionsClass {

    }

    var needInjection = new ChildClass();

对于问题案例: 某些属性应该像下一个例子中那样实现伪接口(或抽象类)。

    class InterfaceClass {
        public cat: string;
        public dog: string;

        public say() {

        }
    }

    @injection(true, InterfaceClass)
    class SingletonClass extends InterfaceClass {
        public cat: string = "Kitty";
        public dog: string = "Hot";

        public say() {
            alert(`${this.cat}-Cat and ${this.dog}-Dog`);
        }
    }

    @injection(true, InterfaceClass)
    class MockInterfaceClass extends InterfaceClass {
        public cat: string = "Kitty";
        public dog: string = "Hot";

        public say() {
            alert(`Mock-${this.cat}-Cat and Mock-${this.dog}-Dog`);
        }
    }

    @injection
    class SimpleClass {
        public say(something: string) {
            alert(`You said ${something}?`);
        }
    }

    @resolve
    class NeedInjectionsClass {
        @inject(InterfaceClass)
        public helper: InterfaceClass;
        @inject(SimpleClass)
        public simpleHelper: SimpleClass;

        constructor() {
            this.helper.say();
            this.simpleHelper.say("wow");
        }
    }

    class ChildClass extends NeedInjectionsClass {

    }

    var needInjection = new ChildClass();

注意: 模拟注入应该在源代码之后定义,因为它重新定义了接口的类创建者

答案 3 :(得分:2)

对于使用Angular2的人,我开发了Fluency Injection https://www.npmjs.com/package/fluency-injection。文档非常完整,它模仿了Angular2的DI的行为。

非常感谢您的反馈,我希望它可以帮助您:)

答案 4 :(得分:2)

您可以使用解决方案:
Lightweight dependency injection container for JavaScript/TypeScript

import {autoInjectable, container} from "tsyringe";

class MyService {
  move(){
    console.log('myService move 123', );
  }
}

class MyServiceMock {
  move(){
    console.log('mock myService move 777', );
  }
}

@autoInjectable()
export class ClassA {
  constructor(public service?: MyService) {
  }
  move(){
    this.service?.move();
  }
}

container.register(MyService, {
  useClass: MyServiceMock
});

new ClassA().move();

输出:

模拟myService move 777

答案 5 :(得分:1)

我一直在开发称为Pigly的DI解决方案。给出了一个有关注入和测试的原始问题的示例(尽管您可以尝试ts-auto-mock as I've done here,但不能自动模拟生成):

给出:

interface IDb {
  set(key: string, value: string);
}

interface IApi {
  setName(name: string);
}

class Api implements IApi {
  constructor(private db: IDb) {}
  setName(name: string){
    this.db.set("name", name);
  }
}

我们可以用

绑定类型
import { Kernel, toSelf, to, toConst } from 'pigly';
import * as sinon from 'sinon';

let spy = sinon.spy();

let kernel = new Kernel();

kernel.bind(toSelf(Api));
kernel.bind<IApi>(to<Api>());
kernel.bind<IDb>(toConst({ set: spy }));

然后使用以下方法解决并测试:

let api = kernel.get<IApi>();

api.setName("John");

console.log(spy.calledWith("name", "John"));

此示例的执行/编译需要一个Typescript转换器-将接口符号和构造函数提供程序编译为纯javascript。 There are a few ways to do this。 ts-node + ttypescript方法是使用tsconfig.json:

{
  "compilerOptions": {
    "target": "es2015",
    "module": "commonjs",
    "moduleResolution": "node",
    "plugins": [{
      "transform": "@pigly/transformer"
    }]
  }
}

并使用

执行
ts-node --compiler ttypescript example-mock.ts

Pigly的区别是不需要对(或第三方)类进行任何更改,但以使用打字稿转换器或如果(如果您不想使用转换器)为更详细的绑定为代价。它仍处于试验阶段,但我认为它显示出了希望。

答案 6 :(得分:0)

TypeScript适用于像requirejs这样的AMD加载器。如果配置正确,TypeScript将输出完全符合AMD标准的javascript。

在测试情况下,您可以配置requirejs以注入可测试模块。

答案 7 :(得分:0)

你可以试一试:https://www.npmjs.com/package/easy-injectionjs。它是一个通用的依赖注入包。

@EasySingleton通过整个应用程序创建一个依赖项的单个实例。它非常适合某种服务。

@EasyPrototype根据需要创建尽可能多的依赖项实例。它是可变依赖项的理想选择。

@EasyFactory主要用于继承:

您可以使用此软件包执行任何操作: 简单用法(来自自述文件):

import { Easy, EasyFactory, EasyPrototype, EasySingleton } from 'easy-injectionjs';

@EasyFactory()
abstract class Person {
  abstract getName();
  abstract setName(v: string);
}

// @EasyObservable()
@EasySingleton()
class Somebody extends Person{
  // @Easy()
  constructor (private name: string) {
    super()
    this.name = 'Sal';
  }

  public getName() {
    return this.name;
  }
  public setName(v: string) {
    this.name = v;
  }
}

@EasyPrototype()
class Nobody extends Person{
  @Easy()
  somebody: Person;
  constructor () {
    super()
  }

  public getName() {
    return this.somebody.getName();
  }

  public setName(v: string) {
    this.somebody.setName(v);
  }
}

@EasyPrototype()
class Data {
  @Easy()
  somebody: Person;
  name: string;

  change(v: string) {
    this.somebody.setName(v);
  }

  getName(): string {
    return this.somebody.getName();
  }
}

let n = new Nobody();
console.log(n.getName()) // Prints Sal
n.setName('awesome');
console.log(n.getName())  // Prints awesome
let d = new Data()
console.log(d.getName())  // Prints awesome
d.change('Gelba')
console.log(n.getName())  // Prints Gelba
d.change('kaa')
console.log(n.getName())  // Prints Kaa

即使你想注入节点模块,你也可以这样做:

import * as IExpress from 'express';
import { Easy, EasySingleton } from 'easy-injectionjs';

@EasySingleton()
class Express extends IExpress {} 

@EasySingleton()
export class App {
  @Easy()
  private _express: Express;
}

let app = new App();
console.log(app)

当然,快速服务器的使用并不适用于控制台日志记录。它仅用于测试:D。

希望有所帮助:D

答案 8 :(得分:-5)

我的AutoFixtureTS工作受到AutoFixture的启发。 AutoFixtureTS通过自动化不相关的测试夹具设置,使TypeScript开发人员更容易进行测试驱动开发,允许测试开发人员专注于每个测试用例的基本要素。

http://ronniehegelund.github.io/AutoFixtureTS/

它仍然只是原型代码,但请查看: - )

/罗尼