
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 это сложно.
Что, если бы мы переписали тест так:
Здесь происходит несколько вещей:
- Тестовый пример излагает предварительные условия на английском языке и делегирует логику создания экземпляра, очищая то, что нам, как читателю теста, не нужно для понимания логики тестового примера. Таким образом, он становится более читабельным и более удобным в обслуживании. Это часто добавляет лаконичности тесту: меньше строк для чтения и понимания. В то же время все, что требует случай, явно. Не нужно переходить к
beforeEachилиasync beforeEach, чтобы узнать, как выглядит настройка.
- Читатель может лучше оценить удобочитаемость здесь. - Он не создает и не отображает наш компонент. Просто проверяет состояние компонента. Нет DOM, более быстрый и менее сложный тест.
- Это дает нам одно место, где мы контролируем контекст каждого тестового примера, и позволяет нам определять точный момент создания нашего тестируемого модуля.
Настройка
Представляем функцию setup. Он заменяет собой весь рабочий процесс тестирования TestBed. Потребуется создать зависимости и создать экземпляр тестируемого класса.
В приведенном выше примере он занимает место getMyComponent(). Использование autoSpy which избавляет от необходимости создавать фиктивные объекты вручную:
- Мы назвали методы нашего объекта-строителя так, чтобы они рассказывали о бизнес-сценариях использования и скрывали реализацию. Читатель все еще может пойти и понять, КАК устанавливается предварительное условие, но он также знает, ЧТО он настраивается. Читатель получает еще один кусочек нашего контекста как автор модульных тестов. Это очень важно для облегчения сопровождения теста.
- В самом тестовом примере объемная реализация не просматривается - еще одна вещь, улучшающая читаемость.
- Здесь мы используем шаблон конструктора для лучшей эргономики разработчика. Кажется, что точечная цепочка - предпочтительный способ использования API.
- Есть одно место для управления всей нашей настройкой. В будущем, когда все будет часто меняться, нам нужно будет менять что-то только в нашей
setupфункции, а не во всех тестовых примерах. - Функция
setupне умеет отображать HTML! Но - «Как я могу проверить логику, не вдаваясь в DOM и HTML?» Сделав состояние компонента нашим интерфейсом с Angular и позволив ему отображать HTML и выполнять манипуляции с DOM. Angular настолько хорош в этом, что нам не нужно этого делать :) - Логику имитации, помещенную в
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для шутки / жасмина
Спасибо за чтение!
Я проводил много модульных тестов, и этот рабочий процесс оказался более эффективным. Попробуйте и скажите, пожалуйста, что вы думаете.