Обзор
В этой статье в основном объясняется идея эффективности и модульности, а не то, как писать модульные тесты для улучшения и улучшения эффективности разработки, стиля кодирования, удобочитаемости кода и эффективности модульных тестов, а не слепо преследовать покрытие.
задний план
-
Теперь многие модульные тесты просто используют аннотацию @Test для тестирования кода или бизнеса во всем интерфейсе запроса.
-
Есть много способов проверить базу данных в одном тестовом процессе, но нет необходимости каждый раз тестировать SQL, потому что SQL-тест должен быть корректным.
-
Неясно, является ли модульное тестирование обязанностью разработки. Модульное тестирование используется для того, чтобы логика кода не изменялась или модифицировалась без ошибок, а не тестировалась.
-
Скорость запуска и эффективность одиночного тестового кода слишком низкие
-
Не удалось пройти весь модульный тест проекта в каждой среде.
-
Метод написан очень крупно, строк много, логика делается при обновлении.
-
Многие компании слепо гонятся за покрытием
Цель
- Повышена эффективность запуска юнит-тестов
- Отдельно от среды, выполнять с уверенностью в каждой среде, не учитывать, есть ли эти данные в тестовой среде и рабочей среде.
- Защитите основную логику метода от неправильного изменения в последующих итерациях.
- Используется для улучшения и повышения эффективности разработки, качества кодирования, удобочитаемости кода, сокращения количества подробных строк кода.
автор
Дзидзо Кельвин
Общественный номер: Кшитигарбха думает
Что такое эффективный модульный тест
Основные ключевые слова:Эффективный, ед.
- эффективный
- единица измерения
- Покройте сцену
- Повторяемый
- утверждение
- Не ищет покрытия
1. Правильное определение
- только тестосновная бизнес-логика, такие как классы логики расчета (расчет индикатора), пользовательское платежное поручение для сборки объектов базы данных и подтаблиц (сумма проверки), объекты для создания таблицы плана закупок и таблицы детализации (количество индикаторов проверки, сумма)
- Неожиданное взаимодействие с пользователем, например экспорт цвета фона данных Excel, запрос записей базы данных.
- Предварительная проверка непроверенной логики
- Не тестируйте вещи, которые явно полезны. Избегайте тестирования классов от сторонних поставщиков, особенно тех, которые предоставляют базовый API платформы, в которой написан код. Например, не тестируйте сторонние библиотеки, такие как добавление элементов в класс Hashtable поставщика, блокировки Redis и т. д.
- Если тестовая среда связана, постарайтесь максимально отделиться от нее, чтобы единый тестовый код мог корректно выполняться в каждой среде.
2. Определение единицы
- «Единица» в «модульном тестировании» означает сделать каждую единицу атомарной и максимально независимой.
- Цель: если метод небольшой, он может быть компонентнымповторное использование, логика обслуживания находится впоследующие итерациине изменено по ошибке. Если он изменен по ошибке, пока каждая итерация проходит один тест всего проекта, об ошибке будет сообщено и найдено.
- сделать метод при написании кодаединственная ответственность, функция метода должна быть небольшой, а логика метода должна быть небольшой. Также не пишите многострочные методы и создавайте длинную логику с несколькими маленькими методами.
- Не пишите большие тесты для одиночных тестов
- Методы логического кода стараются не зависеть друг от друга. Никаких предположений о порядке выполнения тестов не делается.
- Если метод логического кода достаточно мал, его можно легкоустановка/удаление.
- Нет необходимости тестировать весь процесс во время одного теста, то есть не нужно тестировать с первой деловой записи, с которой столкнулся контролер. Тестируется только основная логика, то есть тест каждого маленького метода ядра.
Пример:
public void updateUser(String userName,Integer age) {
// 校验
if (StringUtils.isEmpty(userName)) {
throw new RuntimeException();
}
// redis分布式锁
getRedis();
// 查询主表
selectUser();
// 查询出采买计划Detail
selectUserDetail();
// 对数据进行处理
calculateUserDetailInfo();
// 更新主表
updatePuchasePlanDb();
// 更新detail表
updatePuchasePlanDbDetail();
}
Предполагая, что указанный выше updateUser является первой записью бизнес-логики в контроллере, который используется для обновления информации о пользователе, модульные тесты многих людей будут начинаться с метода updateUser. Но это не соответствует концепции эффективной единицы.
бессмысленный тест
- Из-за сторонних библиотек, таких как redis, мы считаем, что они правильные, и для использования redis вам необходимо подключиться к реальной сети, поэтому это будет зависеть от среды.Если сеть выйдет из строя, код модульного теста будет не проходят.
- Нет необходимости тестировать предварительную проверку, потому что частота ошибок и вероятность будущих изменений относительно малы.
- Запросы к основной таблице, таблице деталей и обновлению базы данных не требуют тестирования, потому что все они являются SQL, а модульное тестирование должно поддерживать одну и ту же логику.Эти SQL могут быть правильно написаны один раз и будут правильными в последующих итерациях.
осмысленный тест
- Поэтому мы проверяем логику обработки данных только при индивидуальном тестировании.Предполагая, что логика обработки данных следующая, мы проведем модульный тест для каждого из следующих трех методов.
- Мы будем думать, что метод на основе компонентов может быть правильным, и комбинация, вероятно, будет правильной. Логика подключаемая, потому что метод достаточно единичный.
- Если его тестировать от начала до конца, вероятность логических изменений в последующих итерациях высока, а также высока вероятность ошибок утверждений, поэтому значимость обслуживания модульных тестов очень мала.
private void calculateUserDetailInfo() {
// 更改公司与关联上下级关系
changeCompany();
// 更改组与关联上下级关系
changeGroup();
// 更改部门与关联上下级关系
changeDeptment();
}
Случаи плохого кодирования:
private void calculateBrand() {
for (int i = 0; i < new ArrayList<>().size(); i++) {
for (int i1 = 0; i1 < new ArrayList<>().size(); i1++) {
if (new Integer(1)==1) {
} else {
}
}
}
}
Такой код не соответствует концепции блока, по крайней мере напишите второй цикл for в другом методе, потому что при модульном тестировании предполагается, что тестовый цикл верен один раз, и утверждается, что цикл правильный с высоким вероятность каждый раз.
3. Покройте сцену
- В дополнение к тестированию нормальных процессов также тестируйте ненормальные процессы.
- Метод переопределения работает нормально, создайте для него один тест
- Переопределить, если еще создать второй модульный тест для того же метода
Случай 1 Аномальный процесс
public Integer logic2(Integer num) throws Exception {
try {
num = this.purchasePlanDetailBusinessImpl.method(num);
} catch (RuntimeException e) {
throw new Exception();
}
return ++num;
}
Затем напишите соответственно:
/**
* 测试异常场景
*/
@Test(expected = Exception.class)
public void testMethodOnException() {
Integer method = this.purchasePlanBusinessImpl.logic(null);
}
4. Утверждение
Модульные тесты многих людей заключаются в том, чтобы распечатать в конце и посмотреть журнал или отладить в консоли, чтобы убедиться, что все правильно, но этого недостаточно.
- Следует судить о возвращаемом значении метода и свойствах-членах в объекте.
- Оцените, пусто ли оно, или оцените значение суммы, или оцените, соответствует ли аномалия ожиданиям.
- Тестируйте только одну вещь за раз. Всего 1 утверждение для проверки одной или нескольких функций/поведений
Таким образом, вместо того, чтобы напрямую судить, пусто ли оно или выводить на терминал
@Test
public void testMethodOnNormal() {
Integer method = this.purchasePlanBusinessImpl.method(1);
Assert.assertTrue(method == 2);
}
5. Повторяемое исполнение
- В настоящее время многие оперативные базы данных или базы данных в оперативной памяти
- Управляйте реальной базой данных, в следующий раз она может быть не подтверждена успешно, потому что данные могут быть обновлены
- Хотя база данных в памяти по-прежнему может восстанавливать данные по умолчанию при следующем запуске, она может управляться чужим одиночным тестом, что приводит к неправильной модификации ее собственных данных.
6. Не гонитесь за освещением вслепую
- Многие компании слепо гонятся за охватом, говоря, что охват Google в США высокий, но у других высокая заработная плата, они уходят с работы вовремя в 6 часов и имеют достаточно времени для разработки.Перед разработкой UML проектирует входные данные и вывод каждого метода.В Китае не так много времени.,все в погоне за быстрым
- На крупных китайских фабриках, хотя и существуют требования к покрытию, покрытие может только доказать, что вы написали модульные тесты, но хорошо ли написаны модульные тесты — это другой вопрос. Потому что некоторые люди помещают нерелевантный код в модульное тестирование, чтобы улучшить покрытие, например, уровень контроллера тестирования, конструктор объекта тестирования, класс инструмента utils и т. д.
- В качестве примера используется Ant Financial. Ant делится на уровни покрытия в зависимости от проекта. Не все проекты имеют высокий уровень покрытия. Как правило, 50 % — это очень много, и оно не обязательно должно быть больше 50 %. в основном способствует эффективной и единицы.
- Согласно эффективному и модульному подходу этой статьи, покрытие будет принесено в жертву, потому что мы тестируем только основную логику.
- Не тратьте время, потому что время итерации китайского интернета очень короткое, поэтому нет необходимости слепо писать юнит-тесты на покрытие, цель юнит-тестов — обеспечить качество кода
Операционный инструмент
- mockito: обычно применимо
- powermock: на основе mockito он может тестировать частные методы и имитировать статические статические методы.
- @InjectMocks используется для того, чтобы фреймворк обновлял объект, нет необходимости вручную создавать основной тестовый класс.
- @Mock: вызывающий класс, возвращаемый установленным ложным объектом
- Assert.assertTrue и т. д.
- JSONObejct, JSONString: иногда ваш собственный фиктивный объект должен быть новым, но в настоящее время нам удобнее написать тело json и преобразовать его в bean-компонент.
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.7.RELEASE</version><!--$NO-MVN-MAN-VER$ -->
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.7.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version><!--$NO-MVN-MAN-VER$ -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.7.RELEASE</version><!--$NO-MVN-MAN-VER$ -->
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
@RunWith(MockitoJUnitRunner.class)
public class PurchasePlanBusinessImplTest {
@InjectMocks
private DoSomeBusinessImpl purchasePlanBusinessImpl;
@Mock
private DoSomeDetailBusinessImpl purchasePlanDetailBusinessImpl;
/**
* 正常流程
*/
@Test
public void testMethodOnNormal() {
Integer method = this.purchasePlanBusinessImpl.method(1);
Assert.assertTrue(method == 2);
}
/**
* 在某些场景
*/
@Test
public void testMethodOnXX() {
when(this.purchasePlanDetailBusinessImpl.method(anyInt())).thenReturn(1);
Integer method = this.purchasePlanBusinessImpl.logic(1);
Assert.assertTrue(method == 3);
}
Оказать влияние
Повышение эффективности
- В настоящее время тест либо запускает службу, либо запускает весну с одного теста, На самом деле это занимает время, будь то 10 секунд или несколько минут, это относительно долго.
- Если используется фиктивный метод, он не обязательно должен основываться на контейнере sring, не требует автоматического внедрения, не требует разрешения отношения bean-компонента и не требует подключения к zk и других проблем среды. Требуется только время запуска1 секунда
- Не нужно создавать данные в реальной базе данных, не нужно писать sql в базе данных в оперативной памяти
- Если протестированы все модульные тесты всего проекта, каждый модульный тест будет загружать базу данных spring и базу данных в памяти один раз, в результате чего для запуска всего проекта потребуется много времени (фактически, как тестовая среда, так и среда регрессии должны запускаться одиночные тесты)
из контекста
- Фактически, каждая среда, будь то совместная отладка разработки, тестирование, проверка или даже производственная среда, выполняет модульное тестирование во время сборки.
- Однако из-за способа использования контейнера Spring для запуска одиночный тест каждого класса должен запускать Spring, что приводит к слишком длительному времени выполнения.
- Или из-за проблемы с данными в среде выполнение не удается
- Смоделированные данные реализованы в коде и работают в памяти, поэтому они не зависят от промежуточного программного обеспечения.
Небольшой метод, небольшое количество строк, небольшая ответственность
- Небольшие обязанности: Из-за правил модульного тестирования, при написании каждого метода сознательно пишите меньше зависимостей.Этот метод подсказывает обзор, который повторно используется другой логикой, а не большой метод, что приводит к использованию схожей логики.Другие коллеги пойдет и скопирует код. (Например, он не будет запрашивать базу данных, проверять и вычислять одним методом)
- Повышает вероятность того, что методы будут использоваться другой логикой, поскольку большие методы трудно использовать повторно.
Технические характеристики
- Класс модульного теста принимает класс реализации тестируемого класса какБазовый плюс Тест, например xxxBusinessImpTest
- Класс модульного теста должен быть помещен в тестовый каталог, а имя пакета такое же, какПуть тестируемого класса непротиворечив, чтобы соответствующий метод не был покрыт, когда идея и сонар запускают проверку покрытия.
- Название метода:начни с теста, добавить имя реального метода, добавить сцену, например testMethodOnNormal, testMethodOnException, testMethodOnLackOfMoney
- Методы обычно имеют возвращаемое значение, даже если возвращаемого значения нет, напишите логическое значение.
- Если это метод процесса и т. д., он может быть недействительным, например, в шаблоне проектирования цепочки ответственности.
- Должен утверждать, а не печатать. Значение утверждения, истинное или ложное, ожидаемое исключение
- По возможности пишите логику проверки данных перед тестируемым методом, тестируемый метод обрабатывает только бизнес-логику и не проверяет данные, чтобы при одиночном тестировании было меньше методов, требующих мока.
- Техническая система компании не обязательно резервирует время для модульного тестирования, и немногие компании готовы тратить время на качество и обслуживание. Все спешат выполнить задание.
- Например, 3 дня разработки, 1 день юнит-тестирования, 1 день сонар+обзор и 1 день совместной отладки, вы можете не успеть это сделать.