Три вопроса о модульном тестировании
Как программист, я более или менее слышал о модульном тестировании, но многие мелкие партнеры не использовали его в реальных проектах. Причина может заключаться в том, что существуют некоторые"непонимание",Например:
- Написание модульных тестов занимает больше времени, мне приходится работать сверхурочно каждый день, чтобы написать код продукта, так как же я могу писать тесты?
- Писать юнит-тесты не очень выгодно, и это не то же самое, что иметь баги;
- Написание юнит-тестов — это бремя: если вы измените структуру кода продукта, вам придется изменить код теста.
Попробуйте сначала ответить на эти вопросы.
Написание модульных тестов занимает больше времени, что является неточным описанием. Если быть точным, написание модульных тестов стоит дороже"время писать код", по этому поводу и говорить нечего, ведь нужно писать больше тестового кода. Но когда программист работает над требованием, он не тратит много времени на написание кода. Вам предстоит разобраться с логикой предыдущего кода, спроектировать классы и методы, а затем написать код.После написания его нужно протестировать вручную.Если есть ошибки, то нужно отлаживать, исправлять и снова тестировать."Времени на написание кода на самом деле очень мало". Использование модульных тестов может занять больше времени для написания кода, но это может помочь вам сократить другое время,"позволит вам тратить меньше времени на это".
Есть ли ошибки в написании модульных тестов? Конечно, может быть, мы не можем добиться полного отсутствия ошибок, но написаны модульные тесты, которые могут значительно уменьшить количество ошибок. Поскольку стоимость написания модульных тестов для поиска ошибок очень низка, они могут найти ошибки на этапе разработки и проверить множество граничных условий. но если ты"Восприятие потребностей и бизнеса изначально неправильно"Да, это не может быть решено с помощью модульного тестирования и, естественно, приведет к ошибкам. Сказав это, преимущества написания модульных тестов выходят далеко за рамки поиска ошибок."документация по коду"функция, и"Рефакторинг сети безопасности"существует. Это может даже помочь вам"потребности управления","код дизайна".
Изменение кода продукта требует сохранения соответствующего тестового кода, что влечет за собой дополнительные расходы. Но с помощью редактора"Функция рефакторинга", вы можете легко изменить места, которые необходимо изменить в пакетном режиме, но стоимость не так велика, как вы думаете. И после рефакторинга снова запустите модульный тест, чтобы увидеть, какие из них не работают.Вы можете дважды проверить код продукта, который вы изменили, чтобы увидеть, есть ли какие-либо проблемы. Если мы думаем о модульном тестировании как"Документация по кодам продуктов"Глядя на это, вероятно, более приемлемо принять стоимость этого обслуживания.
Зачем нужно модульное тестирование?
Как упоминалось ранее, модульное тестирование имеет множество функций. Лично я считаю, что самая большая роль модульного тестирования — это «документация кода» и «подстраховка при рефакторинге». Ведь в длительном процессе разработки программного обеспечения всегда есть изменения и модификации. Если не хватает тестирования, изменить кусок кода, это как разминировать.После изменения у меня всегда барабан в сердце.Перед тем, как выйти в интернет, нужно молча поклониться Богу, боясь вызвать какие-то баги.
Но если тестов (не только юнит-тестов) достаточно, после изменения кода можно снова запустить тесты, чтобы посмотреть, какие из них дают сбой, вызвано ли это вашими собственными изменениями и как исправить тесты. Так я чувствую себя более уверенно.
Вы знаете, код написан для того, чтобы люди его видели. Тестирование более дружелюбно, чем производственный код, потому что оно простое, прямолинейное и описано с точки зрения пользователя, поэтому, если вы хотите знать, какую функцию выполняет часть производственного кода, будет более интуитивно и удобно увидеть его единицу. тесты.
Многие команды будут проводить тестирование, но большая часть работы по тестированию выполняется после разработки, и специальные студенты-тестировщики отвечают за сквозное тестирование или тестирование API. На самом деле стоимость сквозного тестирования очень велика, особенно для определенных граничных условий, очень хлопотно конструировать данные и сцены. И как только ошибка обнаружена, требуется много времени, чтобы сообщить, изменить, отправить и развернуть.
Самым большим преимуществом модульного тестирования является «низкая стоимость».Тестировать каждую ветвь кода продукта проще, а модульное тестирование обычно пишется самими разработчиками, которые могут найти ошибки в кратчайшие сроки и изменить их с наименьшими затратами.
Что такое модульное тестирование?
тестовая пирамида
Не все тесты являются юнит-тестами, существует множество типов тестов. Более широко распространенная в отрасли «Тестовая пирамида» описывает их различия и взаимосвязь:

С точки зрения модели тестовой пирамиды, чем ниже тест, тем шире охват и ниже стоимость. Модульное тестирование находится в нижней части пирамиды тестирования и является основой всей пирамиды тестирования.
Конечно, тестовая пирамида не обязательно имеет только три слоя, и в середине могут быть другие тесты, такие как «контрактное тестирование».
❝Есть и другой способ сказать, что в модели «алмазного теста» большинство тестов написано на уровне тестирования интерфейса, в то время как тесты пользовательского интерфейса и модульные тесты пишут только небольшое подмножество тестов. Эта статья не обсуждает, и заинтересованные студенты могут узнать об этом самостоятельно.
❞
Особенности модульного тестирования
Модульное тестирование, как и его название, «Единица» (Unit), достаточно маленькое, достаточно быстрое и без зависимостей. Модульные тесты проверяют только логику той части производственного кода, которую вы хотите протестировать. Модульные тесты должны проверять только простую бизнес-логику. Вообще говоря, запуск модульного теста выполняется быстро, в основном от нескольких миллисекунд до десятков миллисекунд. Если есть зависимые классы, вы можете имитировать другие классы, чтобы устранить внешние зависимости.
Что не является модульным тестом?
Многие студенты легко путают другие тесты с модульными тестами, наиболее распространенным из которых является интеграционный тест, запускающий контекст Spring. такие как использование@SpringBootTest
Аннотация может запускать контекст Spring, который может проверять, нормально ли внедряются зависимости и другие функции Spring, но однократный запуск занимает много времени (потому что контекст Spring нужно запускать), и это не настоящий " модульный тест», потому что он опирается на среду Spring.
Как писать модульные тесты
Так как же писать модульные тесты? В нашей отрасли существует"TDD"(Test Driven Development) методология. Суть TDD заключается в слове «привод», и его концепция заключается в том, чтобы начать с точки зрения тестирования и управлять кодом продукта посредством тестирования. В пирамиде тестирования модульное тестирование наиболее тесно связано с разработчиками, поэтому «тестирование» здесь обычно относится к модульному тестированию.
TDD грубо делится на следующие этапы:
- Разобраться в потребностях
- Входные и выходные параметры класса и метода проектирования
- написать тестовый код
- выбить код продукта
- Рефакторинг, цикл 3-5 шагов.
Прежде всего, мы должны прояснить требования, потому что только проясняя требования, мы можем гарантировать, что код, который мы используем на основе TDD, соответствует бизнес-ожиданиям. Затем вторым шагом является процесс разработки классов и методов, также известный как список задач. На этом этапе вы можете спроектировать отношения между классами и входными и выходными параметрами метода. На самом деле, без использования TDD есть также первые два шага, но использование TDD может помочь вам лучше начать с точки зрения бизнеса, спроектировать все, что нужно спроектировать в первую очередь, избежать непосредственного написания кода и написать половину его, когда вам захочется. неправильно, меняй.
Шаги 3-5 на самом деле циклический процесс. Поскольку вы, возможно, не уделяли особого внимания формату, стилю и производительности кода в начале написания кода, быстрее написать за один раз и дать тесту пройти. После того, как тест пройден, вы можете вернуться и провести рефакторинг кода, написанного ранее, а затем снова запустить все модульные тесты после рефакторинга, чтобы увидеть, есть ли какие-либо неудачные модульные тесты, чтобы определить, влияет ли рефакторинг на производительность. ожидаемые входные и выходные данные.
Структура модульного теста
Полный модульный тест следует разделить на 4 части:
- Объявления и параметры
- Подготовьтесь к участию и поиздевайтесь
- позвоните по коду продукта
- Валидация, также называемая утверждением
Возьмем, к примеру, Java. Существует несколько фреймворков модульного тестирования, наиболее популярными из которых должны быть JUnit и TestNG. Автор немного больше использует JUnit, а JUnit использует@Test
Аннотируйте метод для объявления теста. Последней версией JUnit является JUnit 5. По сравнению с предыдущей версией, в JUnit 5 сделано много улучшений в параметризованном тестировании, поэтому нам не нужно писать много очень похожих методов тестирования.
❝Что касается параметризованного теста JUnit 5, вы можете проверить официальную документацию, а также соответствующий китайский перевод, который очень легко читается. Вы также можете зайти на мой личный веб-сайт и выполнить поиск «параметризованное тестирование JUnit 5».
❞
В общем, название метода должно быть максимально читабельным, оно может быть длинным, но четко выражать цель теста, например:
@Test
void shouldReturn5WhenCalculateSumGiven2And3() {}
@Test
void should_return_5_when_calculate_sum_given_2_and_3() {}
Конкретное использование camelCase или подчеркивания вполне приемлемо в соответствии со спецификациями вашей команды, и старайтесь поддерживать согласованность всех стилей тестирования. (Лично я предпочитаю символы подчеркивания~)
Входные параметры, как правило, являются базовыми типами или объектами POJO, а некоторые параметры могут быть извлечены в переменные, которые можно использовать позже на этапе проверки.
Если код продукта имеет внешние зависимости, вам нужно использовать макеты для устранения внешних зависимостей. Общие фреймворки Mock включают EasyMock,"Mockito"Подождите, вы можете сравнить различия между различными mock-фреймворками и выбрать подходящий.
Когда многие студенты впервые начали писать юнит-тесты, они не могли понять, зачем им нужны макеты, они чувствовали, что макеты хлопотны и даже немного излишни. На самом деле нет, смысл mock в том, что вы"Вы можете гарантировать, что ваши тесты проверяют только ту часть кода, которую вы хотите протестировать.". Таким образом, если тест не пройден, вы можете знать, что должна быть проблема с тестируемым методом, и это не может быть проблемой внешних зависимостей. Только таким образом мы можем достичь истинной «унификации» и гарантировать, что каждый тест достаточно мал и достаточно чист.
После того, как входные параметры и макеты готовы, будет явно вызван тестируемый метод, который обычно представляет собой простую строку.
Наконец, есть проверка.Существует несколько типов проверки.Наиболее часто используется для проверки того, что параметры соответствуют их ожиданиям. Также иногда бывают пограничные случаи, такие как исключения проверки. Платформы тестирования, такие как JUnit, в основном имеют свои собственные функции проверки, но API относительно прост, и лично мне кажется, что его не особенно легко использовать."AssertJ", мощный, а API удобнее в использовании.
Например:
@Test
void shouldReturnUserWithOrgInfoWhenLoginWithUserId() {
String userId = "userId";
String orgId = "orgId";
User user = UserFactory.getUser(userId);
Org org = OrgFactory.getOrg(orgId);
given(orgService.getOrgById(orgId)).willReturn(org);
UserInfo userInfo = userService.login(userId);
assertEquals(org, userInfo.getOrg());
}
Часто задаваемые вопросы по модульному тестированию
Давайте поговорим о некоторых распространенных проблемах с модульным тестированием.
Сначала написать тесты или сначала написать производственный код?
Все будет хорошо. Хотя есть поговорка, что TDD рекомендует сначала написать тест, а потом уже реализацию. Но многие студенты, которые только начали писать модульные тесты, не привыкли к этому методу. Написание тестов в первую очередь имеет то преимущество, что позволяет вам думать с точки зрения бизнеса при разработке кода, а не с точки зрения реализации. Вы можете попробовать сначала написать тест, а затем написать реализацию, чтобы испытать это чувство.
❝На станции b есть видео, в котором используется TDD для расчета чисел Фибоначчи. Это стандартный процесс TDD. Заинтересованные студенты могут посмотреть. Он называется "TDD fight@Расчет чисел Фибоначчи".
❞
Написание модульных тестов занимает много дополнительного времени?
Собственно об этом и говорилось в начале статьи. Написание модульных тестов требует больше времени на «кодирование», но в целом сокращает общее время цикла разработки требований. Так что написание модульных тестов — это абсолютно «хорошее дело».
Какой код больше всего нуждается в модульном тестировании?
Неуверенный код, логически сложный код, важный код. Например, классы инструментов, сервисный уровень трехуровневой архитектуры, совокупный корень DDD и сервисы домена и т. д. должны написать достаточное количество модульных тестов.
Слишком сложно создать объект параметра?
Построить подходящий входной объект проблематично, особенно некоторые объекты имеют много параметров, и если каждый тест приходится строить с нуля, код теста сильно раздуется и его читабельность будет плохой. В настоящее время вы можете использовать фабричный класс для массового производства объектов. Этот фабричный класс размещается в тестовом каталоге и не повлияет на производственный код. В предыдущем примере UserFactory — это фабричный класс для объектов User.
Каково возвращаемое значение void?
Возвращаемое значение равно void, что указывает на то, что метод не имеет параметров и внутри метода должно быть какое-то поведение."Изменено значение внутреннего свойства", или возможно"Был вызван метод внешнего класса".
Если нужно изменить значение внутри, это можно подтвердить через параметр get объекта. Это проблема в модели предметной области после использования DDD, поскольку возможно, что код продукта не нуждается в раскрытии метода get, но из-за потребностей теста выставляется метод get внутреннего свойства. Хотя вы также можете использовать отражение для получения значения внутренних свойств, это не обязательно. Взвесив все за и против, лучше выставить метод get модели предметной области.
Если вы вызываете внешний метод, вы можете использовать проверку, чтобы проверить, вызывается ли метод, и вы можете использовать захват, чтобы проверить входные параметры вызова других методов. Это также подтверждает, что производственный код работает должным образом.
Как издеваться над статическими методами?
Статические методы не годятся для имитации и требуют специального фреймворка для имитации. Например, PowerMock, JMockit. Вообще говоря, многие методы класса Utils являются статическими, мы часто используем класс времени LocalDateTime для получения текущего времени, которое также является статическим. В настоящее время вам нужно использовать специальную фиктивную структуру, чтобы издеваться над ней.
Как тестируется многопоточность?
Многопоточность также не подходит для тестирования. Если программа простая, можно использовать"спать"илиCountDownLatch
Подождите, пока класс многопоточного инструмента поможет тесту, подождите, пока запустятся все потоки, а затем выполните единую проверку.
Если программа относительно сложная, вам нужно использовать специальный многопоточный фреймворк для тестирования, такой какtempus-fugit,Thread Weaver,MultithreadedTCи проект jcstress OpenJDK.
Что касается того, как использовать конкретную структуру, вы можете написать введение в общие аннотации, когда у вас будет время в будущем. На самом деле так и написано в официальных документах.Можете написать несколько примеров по официальному сайту. Рекомендуемый базовый пакет: junit 5 + mockito + assertj. Что касается статических методов и сред многопоточного тестирования, вы можете узнать о них больше, когда они вам понадобятся.
Об авторе
Я Ясин, колоритный и интересный программист.
Публичный аккаунт WeChat: составлена программа
Персональный сайт: https://yasinshaw.com
Подписывайтесь на мой официальный аккаунт и развивайтесь вместе со мной~
