Тестирование
- Опишите подход Test-Driven Development
- Что такое «пирамида тестирования»?
- Что такое «модульное тестирование»?
- Зачем нужны модульные тесты?
- Какие характеристики хорошего модульного теста Вы знаете?
- Какие существуют шаблоны модульных тестов?
- Что такое «интеграционное тестирование»?
- Чем интеграционное тестирование отличается от модульного?
- Какие существуют виды тестовых объектов?
- Чем stub отличается от mock?
- Что такое «фикстуры»?
- Какие аннотации фикстур существуют в JUnit4 или 5?
- Для чего в JUnit используется аннотация
@Ignore
или@Disabled
? - Какие фреймворки для поддержки автоматизированного приемочного тестирования Вы знаете?
- Какие утилиты для нагрузочного тестирования Вы знаете?
Опишите подход Test-Driven Development
Test-driven development(TDD, Разработка через тестирование) - это стиль разработки, в котором развитие системы определяется тестами в коротких циклах:
- Написать один тест.
- Написать только лишь необходимое количество кода, чтобы тест проходил.
- Провести рефакторинг кода, чтобы сделать его “чистым”.
В языках программирования, таких как Java, такие циклы занимают не более пяти минут. В старых языках, с медленной компиляцией и меньшей поддержкой автоматизации рефакторинга, такой цикл занимает больше времени - около 20 минут.
Что такое «пирамида тестирования»?
«Пирамида тестирования» - метафора, представляющая собой пирамиду, состоящую из разного уровня тестов - модульных, интеграционных, пользовательских. В основе пирамиды модульные тесты, которые занимать 70-80% от общего количества тестов. Далее идут интеграционные в количестве 15-20%. На вершине пирамиды пользовательские (e2e) тесты, которые должны быть в количестве 5%. Такая структура позволяет добиться наибольшего профита от автоматизации тестирования.
Что такое «модульное тестирование»?
Модульное/компонентное тестирование (unit testing) - процесс в программировании, позволяющий проверить на корректность отдельные модули исходного кода программы. Идея состоит в том, чтобы писать тесты для каждой нетривиальной функции или метода. Это позволяет достаточно быстро проверить, не привело ли очередное изменение кода к регрессии, то есть к появлению ошибок в уже оттестированных местах программы, а также облегчает обнаружение и устранение таких ошибок.
Зачем нужны модульные тесты?
- Меньше времени на выполнение функциональных тестов. Функциональные тесты требуют большого количества ресурсов. Как правило, приходится открывать приложение и выполнять ряд действий, чтобы проверить ожидаемое поведение. Тест-инженеры не всегда знают, что это за действия, и им приходится обращаться к специалистам в этой области. Само тестирование может занимать несколько секунд, если это обычные изменения, или несколько минут для более масштабных изменений. Наконец, этот процесс необходимо повторять для каждого изменения, внесенного в систему.Модульные тесты, с другой стороны, занимают миллисекунды, выполняются простым нажатием кнопки и не обязательно требуют знаний о всей системе в целом. Успешность прохождения теста зависит от средства выполнения теста, а не от пользователя.
- Защита от регрессии. Дефекты регрессии вводятся при внесении изменений в приложение. Довольно часто тест-инженеры тестируют не только новую функцию, но и функции, существовавшие до этого, чтобы проверить, что эти функции по-прежнему работают должным образом. С модульным тестированием можно повторно запускать весь набор тестов после каждой сборки или даже после изменения строки кода. Это дает вам уверенность, что ваш новый код не нарушил существующие функциональные возможности.
- Исполняемая документация. Не всегда очевидно, что делает конкретный метод или как он себя ведет при определенных входных данных. Вы можете спросить себя: как поведет себя метод, если я передам ему пустую строку? А значение NULL? Если у вас есть набор модульных тестов с понятными именами, каждый тест сможет четко объяснить, какими будут выходные данные для определенных входных данных. Кроме того, он сможет проверить, что это действительно работает.
- Менее связанный код. Если код тесно связан, он плохо подходит для модульного тестирования. Без создания модульных тестов для кода это связывание может быть менее очевидным. Когда вы пишете тесты для кода, вы естественным образом разделяете его, иначе его будет сложнее тестировать.
Какие характеристики хорошего модульного теста Вы знаете?
- Быстрый. В хорошо разработанных проектах могут быть тысячи модульных тестов. Модульные тесты должны выполняться очень быстро. За миллисекунды.
- Изолированный. Модульные тесты являются автономными, могут выполняться изолированно и не имеют зависимостей от внешних факторов, таких как файловая система или база данных.
- Повторяемый. Запуски модульного теста должны иметь согласованные результаты, то есть всегда возвращать одинаковый результат, если вы не вносите никаких изменений между запусками.
- Самопроверяемый. Тест должен автоматически определять, пройден он или нет, без участия пользователя.
- Уместный. Время на написание модульного теста не должно значительно превышать время написания тестируемого кода. Если вам кажется, что тестирование кода занимает слишком много времени по сравнению с написанием кода, продумайте структуру, более подходящую для тестирования.
Какие существуют шаблоны модульных тестов?
-
AAA (Arrange, Act, Assert) – хороший шаблон для написания модульных тестов. (входные данные, действие, ожидаемый результат). Один модульный тест должен тестировать что-то одно. Следовательно, каждый тест-кейс должен содержать только один AAA-набор. Тест-кейс не должен быть слишком большим (больше 10 строк кода), если он следует шаблону AAA.
-
BDD-style (Given, When, Then)- использует три других ключевых слова для описания каждого тест-кейса: Given, When and Then. Подход “given-when-then” почти аналогичен подходу “arrange-act-assert”. Они оба просто определяют переход из одного состояния в другое в Конечном Автомате (Finite State Machine, FSM).
Отличия AAA и BDD-style:
- BDD-style смотрит на модуль как-бы “снаружи”, т.е фокусируется на его внешнем поведении
- Используя BDD, вы должны определить язык предметной области (domain specific language, DSL) при написании ваших тестовых спецификаций. Из-за этого, обычно требуется использовать другой фреймворк.
Что такое «интеграционное тестирование»?
Интеграционное тестирование (integration testing) — это тестирование, проверяющие работоспособность двух или более модулей системы в совокупности — то есть нескольких объектов как единого блока. В тестах взаимодействия же тестируется конкретный, определенный объект и то, как именно он взаимодействует с внешними зависимостями.
Чем интеграционное тестирование отличается от модульного?
С технологической точки зрения интеграционное тестирование является количественным развитием модульного, поскольку так же, как и модульное тестирование, оперирует интерфейсами модулей и подсистем и требует создания тестового окружения, включая заглушки на месте отсутствующих модулей. Основная разница между модульным и интеграционным тестированием состоит в целях, то есть в типах обнаруживаемых дефектов, которые, в свою очередь, определяют стратегию выбора входных данных и методов анализа.
Допустим, есть класс, который при определенных условиях взаимодействует с web-сервисом через зависимый объект. И нам надо проверить, что определенный метод зависимого объекта действительно вызывается. Если в качестве зависимого класса передать:
- реальный класс, работающий с web-сервисом, то это будет интеграционное тестирование.
- заглушку, то это будет тестирование состояния.
- шпиона, а в конце теста проверить, что определенный метод зависимого объекта действительно был вызван, то это будет тест взаимодействия.
Какие существуют виды тестовых объектов?
пустышка (dummy) - объект, который обычно передается в тестируемый класс в качестве параметра, но не имеет поведения: с ним ничего не происходит и никакие его методы не вызываются.
Примером dummy-объектов являются new object(), null, «Ignored String» и т.д.
фальшивка (fake object) применяется в основном для ускорения запуска ресурсоёмких тестов и является заменой тяжеловесного внешнего зависимого объекта его легковесной реализацией.
Основные примеры — эмулятор базы данных (fake database) или фальшивый web-сервис.
заглушка (test stub) используется для получения данных из внешней зависимости, подменяя её. При этом заглушка игнорирует все данные поступающие из тестируемого объекта, возвращая заранее определённый результат.
Тестируемый объект использует чтение из конфигурационного файла? Тогда передаем ему заглушку
ConfigFileStub
возвращающую тестовые строки конфигурации без обращения к файловой системе.
шпион (test spy) - разновидность заглушки, которая умеет протоколировать сделанные к ней обращения из тестируемой системы, чтобы проверить их правильность в конце теста. При этом фиксируется количество, состав и содержание параметров вызовов.
Если существует необходимость проверки, что определённый метод тестируемого класса вызывался ровно 1 раз, то шпион - именно то, что нам нужно.
фикция (mock object) похож на шпиона, но обладает расширенной функциональностью, заранее заданными поведением и реакцией на вызовы.
Чем stub отличается от mock?
stub используется как заглушка сервисов, методов, классов и т.д. с заранее запрограммированным ответом на вызовы.
mock использует подмену результатов вызова, проверяет сам факт взаимодействия, протоколирует и контролирует его.
Что такое «фикстуры»?
Фикстуры (fixtures) - состояние среды тестирования, которое требуется для успешного выполнения теста. Основная задача фикстур заключается в подготовке тестового окружения с заранее фиксированным/известным состоянием, чтобы гарантировать повторяемость процесса тестирования.
Какие аннотации фикстур существуют в JUnit4 или 5?
@BeforeClass
в JUnit4 /@BeforeAll
в JUnit5 - определяет код, который должен единожды выполниться перед запуском набора тестовых методов.@AfterClass
в JUnit4 /@AfterAll
в JUnit5 - код, выполняемый один раз после исполнения набора тестовых методов.@Before
в JUnit4 /@BeforeEach
в JUnit5 - определяет код, который должен выполняться каждый раз перед запуском любого тестовым методом.@After
в JUnit4 /@AfterEach
в JUnit5 - код, выполняемый каждый раз после исполнения любого тестового метода.
Для чего в JUnit используется аннотация @Ignore
или @Disabled
?
@Ignore
в JUnit4 или @Disabled
в JUnit5 указывает на необходимость пропустить данный тестовый метод.
Какие фреймворки для поддержки автоматизированного приемочного тестирования Вы знаете?
Cucumber, Jbehave, Spock.
Какие утилиты для нагрузочного тестирования Вы знаете?
Apache JMeter, The Grinder, Gatling, HP Perfomance Tester (Load Runner).