Angular TestBed считается вредным!

Почему я не использую Angular TestBed и, возможно, вам тоже не стоит

В этой статье я сделаю краткий обзор TestBed, расскажу о некоторых его свойствах и методах, а затем предложу альтернативу. Альтернатива, которая вернет вам контроль над вашими тестами! И вариант автоматизации тестирования!

Давайте сделаем состояние компонента нашим интерфейсом с помощью Angular, и пусть он отображает HTML и выполняет DOM.

Мне нужно проверить только МОЙ код.

Во время моей практики я обнаружил, что использование TestBed неоптимально, и разработал стратегию, которая действительно хорошо работает для компонентов модульного тестирования, служб и любых других абстракций Angular и не-Angular.

Чем для нас помогает TestBed?

  • Он создает контекст внедрения зависимостей (DI) и позволяет нам переопределять поставщиков, службы и целые модули. Он компилирует, создает экземпляры и отображает в HTML наши компоненты, присоединяя их к экземпляру фикстуры.
  • Он хочет скомпилировать и создать экземпляры наших компонентов, а затем выполнить тесты для DOM. Это заставляет нас, авторов и сопровождающих тестов, запрашивать DOM и работать с HTML, который является результатом Angular. Я считаю это медленным, изворотливым и, что самое главное, не нашей работой. Я верю, что Angular, когда ему дана переменная visible === true для визуализации моего компонента:
  • И скрыть это, когда visible === false. И мне не следует это проверять. Это работа Angular, и я доверяю ей и ее тысячам тестов. Тысячи людей просматривают код фреймворка, есть вероятность, что ошибки будут обнаружены и исправлены. Мне нужно проверить МОЙ код. Это то, что обычно не работает, потому что есть только одна пара глаз и ноль модульных тестов, пока я не создам их.

Я хочу протестировать только наш код и позволить Angular делать свою работу. То есть взять значения и привязать их к HTML. Это означает, что нам не нужно фактически компилировать наши компоненты и иметь TestBed для их рендеринга. Только для создания экземпляра базового класса и тестирования методов и свойств экземпляра.

Такой подход, при котором HTML не отображается, вынуждает нас отказаться от логики в шаблонах, потому что мы не сможем ее протестировать! И отсутствие логики в шаблонах - это очень хорошо!

TestBed не требуется

Если нам не нужно компилировать, создавать экземпляры и рендерить наши компоненты, TestBed не требуется. Но его API удобен с функцией TestBed.get или inject, верно? Это позволяет нам переопределять модули. И так далее. Отчасти это не так, потому что тогда нам нужно заполнить предварительные условия для нашего тестового примера внутри нашего тестового примера. А часто это выглядит так:

Так что это похоже на обычный тестовый пример, не так ли? Похоже, что многие тестовые примеры я написал сам. И хотя он делает свою работу по обеспечению видимости предстоящего книжного тура, есть несколько вещей, на которые он не отвечает, когда должен.

  • Что является предпосылкой? - Есть книга? Имея bookTours? оба? Знаете имя автора?
  • Что мы здесь тестируем? Здесь много беспорядка.
  • Нужно ли нам редактировать все Author экземпляры, если форма класса Author изменится? Каждый тест на авторском компоненте будет нуждаться в одном, верно?
  • Откуда берутся fixture и debugElement? Для новичка в модульном тестировании Angular это сложно.

Что, если бы мы переписали тест так:

Здесь происходит несколько вещей:

  1. Тестовый пример излагает предварительные условия на английском языке и делегирует логику создания экземпляра, очищая то, что нам, как читателю теста, не нужно для понимания логики тестового примера. Таким образом, он становится более читабельным и более удобным в обслуживании. Это часто добавляет лаконичности тесту: меньше строк для чтения и понимания. В то же время все, что требует случай, явно. Не нужно переходить к beforeEach или async beforeEach, чтобы узнать, как выглядит настройка.
    - Читатель может лучше оценить удобочитаемость здесь.
  2. Он не создает и не отображает наш компонент. Просто проверяет состояние компонента. Нет DOM, более быстрый и менее сложный тест.
  3. Это дает нам одно место, где мы контролируем контекст каждого тестового примера, и позволяет нам определять точный момент создания нашего тестируемого модуля.

Настройка

Представляем функцию setup. Он заменяет собой весь рабочий процесс тестирования TestBed. Потребуется создать зависимости и создать экземпляр тестируемого класса.

В приведенном выше примере он занимает место getMyComponent(). Использование autoSpy which избавляет от необходимости создавать фиктивные объекты вручную:

  1. Мы назвали методы нашего объекта-строителя так, чтобы они рассказывали о бизнес-сценариях использования и скрывали реализацию. Читатель все еще может пойти и понять, КАК устанавливается предварительное условие, но он также знает, ЧТО он настраивается. Читатель получает еще один кусочек нашего контекста как автор модульных тестов. Это очень важно для облегчения сопровождения теста.
  2. В самом тестовом примере объемная реализация не просматривается - еще одна вещь, улучшающая читаемость.
  3. Здесь мы используем шаблон конструктора для лучшей эргономики разработчика. Кажется, что точечная цепочка - предпочтительный способ использования API.
  4. Есть одно место для управления всей нашей настройкой. В будущем, когда все будет часто меняться, нам нужно будет менять что-то только в нашей setup функции, а не во всех тестовых примерах.
  5. Функция setup не умеет отображать HTML! Но - «Как я могу проверить логику, не вдаваясь в DOM и HTML?» Сделав состояние компонента нашим интерфейсом с Angular и позволив ему отображать HTML и выполнять манипуляции с DOM. Angular настолько хорош в этом, что нам не нужно этого делать :)
  6. Логику имитации, помещенную в setup, мы уже должны где-то разместить, даже при использовании TestBed. И наличие строителя дает нам это место. Вместо того, чтобы помещать эти макеты в сами тестовые примеры.

Прямое использование зависимостей

Бывают случаи, когда мы можем захотеть напрямую манипулировать зависимостями. Для этого мы можем использовать функцию TestBed.get или inject. setup эквивалент этого - деструктуризация. Продолжая приведенный выше пример, в нашем тестовом примере мы можем деструктурировать и получить service:

Обычно я рекомендую обернуть эти действия по настройке в метод setup, но мир - беспорядочное место, и иногда нам просто нужно работать напрямую с нашими зависимостями.

По умолчанию

Часто есть некоторые задачи инициализации, прежде чем мы сможем считать устройство работающим. Например - создать экземпляры наблюдаемых, заполнить требуемое состояние и т. Д. Результатом является базовая линия, рабочий компонент, который, как мы знаем, не будет бросать, когда мы вызываем для него ngOnInit. Эти задачи я вложил в специальный default метод.

Если бы у нас был BooksComponent, который зависел бы от BooksService вот так:

Нам нужно будет убедиться, что getBooks возвращает некоторый наблюдаемый объект, иначе мы получим исключение.

Вы спросите, почему отдельный метод? Почему бы, например, не стать частью build? Что ж, ответ тривиален - нам может потребоваться действительно протестировать случай, когда возникает исключение, и для этого мы не хотели бы вызывать default, поэтому его наличие дает нам эту возможность.

Почему «настройка»?

В первом примере я тестировал компонент, поэтому изначально я выбрал именно его - getMyComponent. Мы также тестируем услуги, трубы и т. Д.

Кроме того, я хотел автоматизировать обслуживание тестов, чтобы было легче обнаружить функцию, когда она называется настройкой.

Принятие команды

Похоже, это волшебство!

Это никогда не сработает!

Да, я понимаю, что это совсем другое дело, чем то, к чему вы, возможно, привыкли. Я понимаю, что из-за этого функция setup будет казаться чуждой, сложной и опасной. «Похоже на волшебство!» Мой коллега Симон Казаков сказал мне, когда впервые увидел это.

Я пойму это, если вы не хотите ставить под угрозу рабочий процесс своей команды.

И, конечно, я был бы рад, если бы вы попробовали это и дали мне свой отзыв.

Совместимость

О, и это тоже работает бок о бок с TestBed. Вы можете использовать оба или полагаться на setup для получения зависимостей для тестового модуля.

Автоматизация

Наличие функции setup позволяет немного автоматизировать работу. Я создал инструмент (SCuri) и расширение VS Code для него, что упрощает создание и обновление модульных тестов. Он удаляет шаблонную часть выполнения модульных тестов и оставляет вам ту часть, которую можете делать только вы. Boilerplate:

  • сгенерируйте функцию setup и метод build
  • обновить функцию setup и метод build, добавив новые зависимости
  • добавьте тестовый пример для каждого общедоступного метода вашего класса
  • генерировать autoSpy для шутки / жасмина

Спасибо за чтение!

Я проводил много модульных тестов, и этот рабочий процесс оказался более эффективным. Попробуйте и скажите, пожалуйста, что вы думаете.