Мы знаем (благодаря приведенным выше разделам), что метод resolve() можно использовать для получения динамически инстанцированного класса. Мы также знаем, как описано здесь, что мы можем передать уникальный идентификатор контекста для управления жизненным циклом DI-Container поддерева DI-Container. Как мы можем использовать это в системе тестирования?
# Тестирование
Автоматизированное тестирование считается неотъемлемой частью любой серьезной разработки программного обеспечения. Автоматизация позволяет легко и быстро повторять отдельные тесты или группы тестов во время разработки. Это гарантирует соответствие релизов целевым показателям качества и производительности. Автоматизация помогает увеличить охват и создать более быстрый цикл обратной связи для разработчиков. Автоматизация повышает производительность отдельных разработчиков и обеспечивает выполнение тестов на критически важных этапах цикла разработки, таких как тестирование исходного кода, интеграция функций и выпуск.
Такие тесты часто бывают разных типов, включая модульные тесты, сквозные (e2e) тесты, интеграционные тесты и т.д. Несмотря на их несомненные преимущества, они могут быть громоздкими в настройке. Компания Nest стремится продвигать передовые методы разработки, которые включают эффективное тестирование. Именно поэтому мы предлагаем следующие функции, помогающие разработчикам и командам создавать и автоматизировать тесты. Гнездо:
- Автоматически создает стандартные модульные тесты для компонентов и e2e-тесты для приложений.
- Предоставляет стандартные инструменты (например, тестовый загрузчик, который создает изолированный блок/загрузчик приложения)
- Предлагает интеграцию с Jest
Как уже упоминалось ранее, вы можете использовать любую систему тестирования, которая вам нравится, поскольку Nest не предписывает использование каких-либо конкретных инструментов. Просто замените необходимые компоненты (например, Test Runner), и вы продолжите пользоваться преимуществами встроенных инструментов тестирования Nest.
# Установка
Чтобы начать работу, сначала установите необходимый пакет:
В следующем примере мы тестируем два класса: CatsController и CatsService. Как уже упоминалось, Jest
(откроется в новом окне) предоставляется в качестве основы тестирования по умолчанию. Он используется для запуска тестов, а также предоставляет функции assert и утилиты-двойники тестов, помогающие в работе с mocking, spying и т.д. В следующем базовом тесте мы вручную инстанцируем эти классы и убеждаемся, что тестер и сервис выполняют свой API-контракт.
Храните файлы с тестами рядом с тестируемыми классами. Файлы тестов должны иметь расширение .spec или .test.
Поскольку приведенный выше пример тривиален, мы на самом деле не тестируем ничего специфического для Nest. Более того, мы даже не используем инжекцию зависимости (обратите внимание, что мы передаем экземпляр CatsService в наш catsController). Такая форма тестирования — когда мы вручную инстанцируем классы, которые хотим протестировать — часто называется изолированным тестированием, потому что оно не зависит от контекста. Давайте рассмотрим некоторые из расширенных функций, которые можно использовать для тестирования приложений, более полно использующих возможности Nest.
# Утилиты для тестирования
Пакет @nestjs/testing предоставляет набор утилит, которые обеспечивают более надежный процесс тестирования. Давайте напишем предыдущий пример, используя встроенный тест :
Класс Test подходит для создания структуры выполнения приложения, которая в основном имитирует среду выполнения Nest, но предоставляет крючки для управления экземплярами класса, включая имитацию и переопределения. В классе Test есть метод createTestingModule(), который принимает в качестве аргумента объект метаданных модуля (тот же объект, который вы передаете декоратору @Module()). Этот метод возвращает экземпляр TestingModule, который, в свою очередь, предоставляет несколько методов. Для модульных тестов важен метод compile(). Этот метод загружает модуль с его зависимостями (аналогично тому, как приложение загружается в обычный файл main.ts с помощью NestFactory.create()) и возвращает модуль, готовый к тестированию.
Метод compile() является асинхронным и поэтому должен быть await. После компиляции модуля вы можете получить все объявленные в нем статические экземпляры (контроллер и провайдер) с помощью метода get().
TestingModule наследуется от эталонного класса модуля и поэтому способен динамически разрешать реплицированные провайдеры (переходные или в области действия запроса). Для этого используется метод resolve() (метод get() может получить только статические экземпляры).
Метод resolve() возвращает уникальный экземпляр провайдера из его собственного поддерева DI контейнера. Каждое поддерево имеет уникальный идентификатор контекста. Поэтому если вы вызовете этот метод несколько раз и сравните ссылки на экземпляры, вы обнаружите, что они не одинаковы.
Подробнее об эталонных атрибутах для единиц измерения читайте здесь.
Вместо того чтобы использовать производственную версию провайдера, вы можете перезаписать ее пользовательским провайдером для целей тестирования. Например, вы можете имитировать службу базы данных вместо подключения к реальной базе данных. Мы рассмотрим переопределения в следующем разделе, но они также доступны для модульного тестирования.
Если документация была полезной и нужной, вы можете поддержать автора несколькими кружками пива 🍺 через donatty. Или внесите свой вклад в пул переводов, сделав ретвит на гитхабе
Я хотел бы сказать вам, что существуют разные стратегии. Мы можем выбирать между горизонтальным тестированием E2E и вертикальным тестированием E2E. Обе стратегии стоит рассмотреть, поскольку они позволяют протестировать различные сценарии. Давайте выясним, что делает каждая стратегия.
Пирамида тестирования
Рассмотрим пирамиду тестирования, которая показывает распределение типов тестов в приложении.
Написание и проведение E2E-тестов требует времени и ресурсов. Например, Google предлагает распределение 70/20/10: 70% модульных тестов, 20% интеграционных тестов и 10% тестов E2E. Точное сочетание будет разным для каждого проекта, но в целом форма пирамиды должна быть сохранена.
Из пирамиды, как мы знаем, следует, что необходимо написать небольшое количество тестов по следующим причинам:
1. хорошо написанных интеграционных и модульных тестов должно быть достаточно. Испытания E2E должны проверить правильность подключения всех элементов.
2. скорость выполнения. Если их сотни, например, юнит-тесты и интеграционные тесты, то тесты будут занимать очень много времени.
3. непредсказуемость. Так как пользовательская среда имитируется, некоторые тесты могут не уложиться в отведенное время из-за API браузера. Это может быть не так с модульными тестами (интеграционными тестами).
Когда использовать e2e тесты
В целом, e2e тесты должны использоваться в каждом проекте, в котором есть команда (Dev, Qa, PM). Однако следует отметить, что e2e-тесты следует использовать только тогда, когда охвачены модульные и интеграционные тесты, поскольку только тогда станет ясно, что еще не охвачено в системе. Вам также следует подумать о написании тестов. Если проект короткий, нет необходимости писать тесты от начала до конца, так как большинство функций можно переписать и пересмотреть.
Прежде чем говорить о сквозных тестах, важно понять, что подразумевается под тестом и какое место он занимает. Классификация тестов очень обширна, и мы не будем перечислять их все.
Знание системы
Самым высоким уровнем в иерархии подходов к тестированию является понятие типа, которое может включать в себя несколько связанных между собой методов тестирования. То есть, один тип теста может соответствовать нескольким типам. Давайте сначала рассмотрим несколько типов тестов, которые отличаются знанием внутренней структуры тестового объекта.
- Метод «черного ящика». Тестирование «черного ящика», также известное как тестирование на основе спецификации или поведенческое тестирование, — это метод тестирования, основанный исключительно на внешних интерфейсах тестируемой системы. Зачем нужно тестирование «черного ящика»? Тестируемая программа похожа на непрозрачный черный ящик, содержимое которого испытатель не может увидеть. Цель этой техники — поиск ошибок в следующих категориях: неправильная реализация или отсутствие функций; ошибки интерфейса; ошибки в структурах данных или в организации доступа к внешним базам данных; поведенческие ошибки или низкая производительность системы; таким образом, мы не имеем представления о структуре и внутренней организации системы. Мы должны сосредоточиться на том, что делает программное обеспечение, а не на том, как оно это делает.
- Метод «белого ящика». Тестирование «белого ящика» (также: прозрачное, открытое, стеклянное, основанное на коде или структурное тестирование) — это метод тестирования программного обеспечения, который предполагает, что внутренняя структура/дизайн/реализация системы известны тестировщику. Мы выбираем входные значения на основе наших знаний о коде, который будет их обрабатывать. Точно так же мы знаем, каким должен быть результат этой обработки. Для этой техники необходимо знание всех особенностей тестируемой программы и ее реализации. Тестирование «белого ящика» — это погружение во внутреннюю структуру системы, помимо ее внешних интерфейсов. Почему «белая коробка»? Для испытателя тестируемая программа — это прозрачная коробка, содержимое которой он хорошо видит.