Я изучаю возможности делать TDD с помощью TypeScript. Если я напишу свои тесты в TypeScript, можно ли заставить операторы импорта возвращать mocks для моего тестируемого класса? Или это единственный возможный подход, чтобы писать тесты в чистом javascript и заниматься инъекцией AMDs?
Впрыск зависимости в TypeScript
Ответ 1
Я использую infuse.js для инъекций зависимостей в TypeScript.
Ссылка на 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). Несмотря на это, он очень хорошо работает в моих приложениях.
Ответ 2
Я разработал контейнер IoC под названием InversifyJS с расширенными функциями впрыска зависимостей, такими как контекстные привязки.
Вам необходимо выполнить 3 основных шага:
1. Добавить аннотации
API аннотации основан на Angular 2.0:
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(); };
}
2. Объявлять привязки
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;
3. Разрешить зависимости
API-интерфейс разрешения основан на Ninject:
import kernel = from "./inversify.config";
var ninja = kernel.get<INinja>("INinja");
expect(ninja.fight()).eql("cut!"); // true
expect(ninja.sneak()).eql("hit!"); // true
Последняя версия (2.0.0) поддерживает множество вариантов использования:
- Модули ядра
- промежуточное ПО ядра
- Использование классов, строковых литералов или символов в качестве идентификаторов зависимостей
- Инъекция постоянных значений
- Впрыскивание конструкторов классов
- Инъекция фабрик
- Авто factory
- Инъекция поставщиков (async factory)
- Обработчики активации (используемые для ввода прокси)
- Несколько инъекций
- Отмеченные привязки
- Декодеры пользовательских тегов
- Именованные привязки
- Контекстные привязки
- Дружественные исключения (например, круговые зависимости)
Подробнее об этом можно узнать на https://github.com/inversify/InversifyJS
Ответ 3
Попробуйте Инъектор зависимостей (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();
Примечание: Вредоносная инъекция должна определяться после исходного кода, поскольку маска переопределяет класс-создателя для интерфейса
Ответ 4
Для людей, которые используют Angular2, я разработал Fluency Injection https://www.npmjs.com/package/fluency-injection. Документация достаточно полная и имитирует поведение Angular2 DI.
Обратная связь очень ценится, и я надеюсь, что это вам поможет:)
Ответ 5
TypeScript хорошо работает с загрузчиками AMD, такими как requirejs. Если он корректно завершен, TypeScript будет выводить полностью совместимый с AMD javascript.
В тестовой ситуации вы можете настроить requirejs для ввода тестируемых модулей.
Ответ 6
Я работаю над AutoFixtureTS, вдохновленным AutoFixture. AutoFixtureTS упрощает разработку TypeScript разработчиками Test-Driven Development, автоматизируя не относящуюся к делу установку Test Fixture, позволяя разработчику теста сосредоточиться на необходимости каждого тестового примера.
http://ronniehegelund.github.io/AutoFixtureTS/
Его еще только прототип кода, но проверьте его: -)
/Ronnie