Могут спросить: а действительно ли нам нужен специальный инструмент для создания макетов объектов в TypeScript, в то время как есть несколько надежных библиотек, написанных на JavaScript? Мы можем использовать Mocha или Jasmine в качестве тестового фреймворка, мы также можем использовать Karma и Chai - все они являются библиотеками JavaScript и успешно работают, так почему бы нам не использовать Sinon.JS для издевается?

Все дело в типах

Разработка довольно большого приложения (более 500 классов и около 2000 тестов), написанного с нуля на TypeScript, дала мне возможность испытать полный спектр проблем, возникающих из-за использования не полностью типизированной макетной среды. Самые болезненные вопросы связаны с автоматическим рефакторингом. Когда объекты, используемые в тестах, не типизированы (и IDE не может строго определять типы), IDE не сможет определить, какие из них следует изменить, когда мы переименовываем метод в одном из них. . Другими словами: допустим, у нас есть более десяти классов, содержащих метод с именем load(). Когда мы попытаемся переименовать один из них, IDE изменит всю фиктивную конфигурацию, связанную с оставшимися девятью вхождениями load(), любезно прервав десятки тестов.

Повторяющиеся проблемы с фреймворком mocking вынудили нашу команду искать более качественные и удобные для TypeScript инструменты. Когда мы начинали наше приключение с TypeScript более двух лет назад, не было фреймворков, посвященных этому блестящему языку. Более того, язык появился сразу после выпуска 1.5, который принес революционные изменения, а IDE даже не поддерживала предложения по импорту ES6 - мы были вынуждены писать их вручную. К счастью, ситуация значительно изменилась, и теперь мы можем выбрать одну из нескольких альтернатив, написанных с учетом TypeScript. Давайте сравним два из них.

typemoq против ts-mockito



typemoq кажется одной из самых популярных имитирующих библиотек, созданных для TypeScript. Его синтаксис во многом вдохновлен библиотекой moq .NET.



ts-mockito имеет удивительный синтаксис, вдохновленный Java Mockito, одним из самых популярных фреймворков для имитации Java.

Создание макета

Допустим, у нас есть класс TestedClass, у которого есть зависимость Bar, которую мы хотели бы высмеять в наших тестах.

class Bar {
  constructor() {
    console.info('constructor called!');
  }
  public getValueBy(count:number):string {
  }
  public saveState(state:SomeSate):void {
  }
}

Чтобы создать фиктивный объект для класса в typemoq, мы используем следующий синтаксис:

// typemoq
const bar:TypeMoq.IMock<Bar> = TypeMoq.Mock.ofType(Bar);
// prints 'constructor called!'
const tested:TestedClass = new TestedClass(bar.object);

Стоит отметить, что когда мы создаем макет, typemoq выполняет конструктор макета класса, что в некоторых случаях может быть крайне нежелательным.

В ts-mockito мы набираем:

// ts-mockito
const bar:Bar = mock(Bar);
const tested:TestedClass = new TestedClass(instance(bar));

Макет настройки

Чтобы фиктивный класс возвращал определенное значение для заданных параметров, мы будем использовать следующий синтаксис в typemoq:

// typemoq
bar
  .setup(bar => bar.getValueBy(TypeMoq.It.isValue(12)))
  .returns(() => 'twelve');

Того же поведения мы можем добиться в ts-mockito, используя следующий синтаксис:

// ts-mockito
when(bar.getValueBy(12)).thenReturn('twelve');

Проверка звонков

Часто нам нужно проверить, был ли вызван какой-либо метод со структурой данных, которая полностью совпадает с той, которую мы создали в тесте:

// typemoq
bar.verify(
  (bar) => bar.saveState(TypeMoq.It.isValue(state)),
  TypeMoq.Times.once()
);
// ts-mockito
verify(bar.saveState(deepEqual(state))).once();

Проверка типов и предложения

Обе библиотеки спроектированы таким образом, чтобы обеспечивать безопасность типов и предоставлять наилучшие возможные варианты типов IDE.

Автозаполнение IDE - одно из основных преимуществ разработки на языке со статической типизацией. Выбирая такой язык, мы хотим получить от этой функции как можно больше прибыли. Тесты не должны быть исключением.

Расширенные возможности

Обе библиотеки могут:

  • настраивать сложные макеты поведения с помощью пользовательских функций,
  • определить общий тип ожидаемого аргумента метода (любое число, любая строка, что угодно),
  • запись вызовов методов (получение аргументов, переданных вызываемому методу),
  • проверить количество вызовов с заданными аргументами,
  • проверить порядок вызова метода,
  • фиктивные геттеры,
  • настроить другое поведение для последующих вызовов методов,
  • запускать как в браузере, так и в node.js,
  • работать с компиляцией, предназначенной как для ES5, так и для ES6.

Возможности, доступные только в typemoq:

  • имитирующие интерфейсы (требуется поддержка ES6 Proxy),
  • частичное издевательство,
  • создание глобальных моков и их песочница,
  • издевательство над существующими объектами (экземплярами),
  • имитирует автономные функции (ts-mockito позволяет имитировать только классы),
  • проверка вызовов сеттера,
  • указание фиктивной настройки как ожидание (проверка всех сразу).

Заключение

Обе библиотеки заслуживают нашего внимания. typemoq имеет впечатляющее количество расширенных функций, которые делают эту библиотеку инструментом для действительно сложных имитаторов. ts-mockito привлекает прекрасным синтаксисом, который позволяет забыть о том, чтобы заглядывать в документацию библиотеки. Синтаксис ts-mockito также более значим. Это важно, особенно когда мы пишем наши тесты с убеждением сделать их живой документацией нашего кода. Нельзя отрицать, что typemoq имеет вдвое больше расширенных функций, чем его соперник. Тем не менее, мой опыт ясно показывает, что более простое использование дает гораздо больше прибыли в повседневном использовании - магическое издевательство требуется редко, и его лучше избегать. Поэтому выбрала ц-мокито.

Бонус

Я создал два репозитория, демонстрирующих настройку и простое использование обеих библиотек. Проекты доступны на github:

Https://github.com/michalstocki/typemoq-example
https://github.com/michalstocki/ts-mockito-example