Делитесь техническими знаниями в области Java, .NET, Javascript, эффективности, разработки программного обеспечения, языков программирования и т. д.
эта статьяGitHub TechShareВ комплекте, без рекламы, просто для удовольствия.
Введение
Для программистов модульное тестирование является очень важным навыком, но во многих реальных процессах разработки проектов модульное тестирование часто игнорируется, что приводит к тому, что некоторые люди игнорируют его важность, поэтому, пожалуйста, позвольте автору сначала поворчать несколько слов о его важности, если читатели и друзья уже понимают эту часть содержания, они могут сразу перейти кDAO 层测试часть.
О некоторых основах написания юнит-тестов я в этой статьеМодульное тестирование JavaОн был представлен в PPT, и эта серия статей не будет повторять их, а направлена на то, чтобы объяснить, как писать модульные тесты в проектах уровня предприятия.
В этой серии планируется написать три статьи: тестирование уровня DAO, тестирование уровня бизнес-логики и тесты эффективного написания.
Почему модульное тестирование важно?
Почему вы судите, является ли программист средним или продвинутым уровнем?Вы можете увидеть, может ли ТА писать модульные тесты, потому что написание модульных тестов не только о написании тестового кода, но также часто требует повторного изучения самого кода продукта, и рефакторинг, когда это необходимо.Только тогда вы можете продолжать писать тесты, и вы всегда можете испытать программные идеи высокой связности и низкой связанности в процессе написания кода модульного теста. Написание модульных тестов фактически добавляет к объектной модели специального пользователя. Этот процесс также заставляет нас проектировать объектную модель так, чтобы ее было проще использовать. В общем, написание модульных тестов может углубить понимание высокой связности, низкой связанности, интерфейсно-ориентированного программирования. , Внедрение зависимостей, Дизайн API, Единая ответственность и т. д.编程思想的掌握, поэтому модульное тестирование настолько важно, что его можно очень хорошо использовать для оценки уровня навыков инженера или, по крайней мере, его мышления в области программирования.
Почему модульное тестирование так сложно реализовать?
Многим малым предприятиям трудно внедрять модульное тестирование по следующим причинам: написание модульных тестов занимает много времени, модульные тесты не так просто написать, как они думают, и у разработчиков нет всестороннего понимания модульного тестирования. . Есть и худшие ситуации, такие как недостаточное завершение самого кода, отсутствие проверки данных и проверки бизнес-логики на стороне сервера за функцией CRUD, а также сама система уязвима.Для написания модульных тестов требуется много работы по модификации кода. в этом случае я хотел бы назвать это «полем модульного тестирования»戴维斯双杀"Эффект - поскольку сам код имеет низкую степень завершенности, исходный совершенный код должен иметь определенную степень сложности, но при разработке он предельно прост и отвечает только поверхностным функциям. Разработчики считают, что слишком простой код не пишет необходимые модульные тесты, что приводит к двойному негативному циклу снижения качества кода продукта и искаженному пониманию модульного тестирования.
Модульное тестирование трудно реализовать, что в целом можно отнести к причинам возможностей разработки и культуры качества продукта.Для компаний, которые хотят развиваться долго, они, несомненно, будут ориентироваться на эти два аспекта.Поэтому большинство компаний постоянно преодолевают эти проблемы и улучшение инженеров.Возможность повысить качество и эффективность написания модульных тестов, и в то же время установить правильную концепцию качества продукта.Вы должны знать, что даже если вы потратите дополнительное время на написание модульных тестов, общие временные затраты не увеличится, потому что "质量是免费的», качество продукта улучшилось, а время на устранение неполадок и исправление ошибок, естественно, уменьшилось.
Внедрение культуры качества продукта на предприятии и улучшение способности разработчиков писать модульные тесты, если они достигнуты, внедрение модульных тестов не так сложно, как можно себе представить.
Основы модульного тестирования
Прежде всего, необходимо прояснить некоторые основные принципы модульного тестирования.Отличное модульное тестирование имеет следующие характеристики:
- автоматический, повторяемый
- легко выполнить
- Однажды написанный, он может быть использован в будущем
- Любой может бежать
- Запуск одним нажатием кнопки
- может быстро бегать
Модульные тесты — это не временный код, который пишется для проверки функций, а должен соответствовать принципам AIR, поэтому для их написания требуются определенные навыки.
Принцип AIR упоминается в спецификации Java от Али, и другие сопутствующие спецификации также заслуживают изучения. Я цитирую ее здесь для удобства читателей и друзей. Полную спецификацию можно посмотреть на Github по адресу:Модульные тесты p3c.
- [Обязательно] Хорошие модульные тесты должны соответствовать принципам AIR.
иллюстрировать: Когда юнит-тест запускается на линии, такое ощущение, что воздуха (AIR) не существует, но это очень критично в гарантии качества теста. На макроуровне хороший модульный тест обладает характеристиками автоматизации, независимости и повторяемости.
- А: автоматический
- Я: независимый
- R: Повторяемый (повторяемый)
-
[Обязательно] Модульные тесты должны быть полностью автоматизированными и неинтерактивными. Тестовые случаи обычно выполняются на регулярной основе, и процесс выполнения должен быть полностью автоматизирован, чтобы иметь смысл. Тест, вывод которого требует ручной проверки, не является хорошим модульным тестом. System.out нельзя использовать для проверки человеком в модульных тестах, и для проверки необходимо использовать assert.
-
[Обязательно] Держите модульные тесты независимыми. Чтобы модульные тесты были стабильными, надежными и простыми в обслуживании, сценарии модульных тестов не должны вызывать друг друга и зависеть от порядка выполнения.
Контрпример: метод2 должен полагаться на выполнение метода1 и использовать результат выполнения в качестве входных данных для метода2.
- [Обязательно] Модульные тесты могут выполняться многократно, и на них не может влиять внешняя среда.
иллюстрировать: Модульные тесты обычно помещаются в непрерывную интеграцию, и модульные тесты выполняются каждый раз, когда код возвращается. Если модульный тест зависит от внешней среды (сети, службы, промежуточного ПО и т. д.), это легко приведет к недоступности механизма непрерывной интеграции.
Положительный пример: Чтобы не подвергаться влиянию внешней среды, требуется изменить зависимость SUT на инъекцию при проектировании кода, а также использовать DI-фреймворк, такой как spring, для внедрения локальной реализации (памяти) или реализации Mock во время тестирования.
- [Обязательно] Для модульного тестирования убедитесь, что степень детализации теста достаточно мала, чтобы помочь выявить проблемы. Детализация одного теста находится не более чем на уровне класса и, как правило, на уровне метода.
иллюстрировать: Только при небольшой степени детализации теста можно определить местонахождение ошибки как можно скорее при возникновении ошибки. Модульные тесты не несут ответственности за проверку логики межклассового или межсистемного взаимодействия, что является областью интеграционного тестирования.
- [Обязательно] Добавочный код основного бизнеса, основного приложения и основного модуля обеспечивает прохождение модульного теста.
иллюстрировать: Новый код своевременно дополняет модульный тест. Если новый код влияет на исходный модульный тест, своевременно исправьте его.
Разобравшись с этими основными спецификациями, давайте начнем писать модульные тесты.
Идеальное модульное тестирование должно проверять каждый уровень кода в проекте.В этой статье мы начнем со слоя DAO.
Модульное тестирование уровня DAO
Уровень DAO трудно тестировать из-за его зависимости от базы данных.В этой главе вы найдете некоторые методы тестирования уровня DAO.
Взяв за пример базу данных MySQL, при запуске модульного теста уровня DAO мы не можем полагаться на внешнюю базу данных, потому что это разрушит принцип I (независимости) в принципе AIR.Есть несколько способов освободить зависимость:
- Используйте встроенную базу данных: h2, moby и т. д.
- Используйте Testcontainers, чтобы создать базу данных для модульного тестирования в докере и уничтожить ее после выполнения.
Используйте встроенную базу данных
Сначала рассмотрим первый способ, взяв в качестве примера h2.
Во-первых, в конфигурации приложения модульного тестированияapplication.ymlИзмените источник данных в h2.
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:ufo
Если мы используем JPA для сохранения данных, просто настройте их, и JPA автоматически создаст базу данных в базе данных h2.
Если вы используете MyBatis, вам необходимо указать схему базы данных и добавить следующую конфигурацию:dataИнициализируйте сценарий для данных.
spring:
datasource:
schema: classpath:db/schema-h2.sql
data: classpath:db/data-h2.sql
Если наш проект очень маленький, этого способа достаточно, но когда база данныхschemaКогда он будет становиться все больше и больше, поддержка sql станет трудоемкой задачей, поэтому нам нужно ввести механизм контроля версий для базы данных.
Контроль версий базы данных
Управление версиями базы данных может быть выполнено с помощьюflywayилиliquibase, для использования liquibase вы можете обратиться к тому, что я написал5 минут, чтобы получить контроль версий базы данных Liquibase.
Взяв в качестве примера ликвибазу, после введения мыapplication.ymlС аналогичной модификацией, как показано ниже, среда выполнения модульного теста сначала инициализирует базу данных через liquibase.
spring:
liquibase:
change-log: classpath:liquibase/master.xml
contexts: unit_test
enabled: true
Обратите внимание, что sql контроля версий, который мы написали в это время, подготовлен для базы данных MySQL.Если он используется непосредственно для базы данных h2, существует высокая вероятность возникновения проблем с совместимостью.В настоящее время у нас есть следующие варианты:
- Слегка изменен sql-скрипт для MySQL для инициализации базы данных h2.
- Используйте XML-синтаксис Liquibase для определения структуры базы данных, уменьшая проблемы совместимости.
- использовать вместо этого
TestcontainersВспомогательный тест.
Первые два варианта требуют большой работы, так что давайте посмотрим, что получится с Testcontainers.
Использование тестовых контейнеров
Testcontainersявляется поддержкойJUnitБиблиотека с открытым исходным кодом для тестирования, которую можно использоватьDockerконтейнер получить即用即丢возможности базы данных.
Мы пропустили другие варианты и выбрали сразу Testcontainers, потому что есть некоторые недостатки использования встроенной базы данных, например, если уровень DAO использует синтаксис конкретной базы данных, модульный тест вообще не пройдет, что практически невозможно в enterprise- проектов уровня.Неизбежно, если только это не особо самостоятельный одиночный сервис.
Модульные тесты уровня DAO не должны полагаться на внешние базы данных, но также должны использовать рабочую среду во время выполнения, чтобы результаты тестирования были более точными.
Используйте тестконтейнеры и不符合 AIR 原则, поскольку он опирается на среду Docker, если его запустить на машине без среды Docker, тест завершится неудачей, и он не соответствует требованиям для возможности быстрого запуска, в конце концов, он занимает определенное количество времени. для инициализации контейнера Docker.
Давайте посмотрим, как использовать Testcontainers в нашем коде.
Сначала определите базовый класс абстрактного теста, от которого наследуются конкретные модульные тесты.Следующий метод записи может гарантировать, что база данных будет инициализирована только один раз.
@SpringBootTest
@ContextConfiguration(initializers = AbstractUnitTest.DockerMySQLDataSourceInitializer.class)
public abstract class AbstractUnitTest {
private static final MySQLContainer<?> mysql;
static {
mysql = new MySQLContainer<>("mysql:8.0.11")
.withDatabaseName("dbname");
mysql.start();
}
public static class DockerMySQLDataSourceInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(@NotNull ConfigurableApplicationContext applicationContext) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
applicationContext,
"spring.datasource.url=" + mysql.getJdbcUrl(),
"spring.datasource.username=" + mysql.getUsername(),
"spring.datasource.password=" + mysql.getPassword(),
"spring.datasource.driver-class-name=" + mysql.getDriverClassName()
);
}
}
}
Когда все будет готово, выполнение модульного теста сначала создаст контейнер Docker, а затем запустит liquibase для инициализации базы данных.
Давайте посмотрим на пример простого теста слоя DAO.
@SpringBootTest
@RunWith(SpringRunner.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class NavSiteRepositoryTest extends AbstractUnitTest {
@Autowired
NavSiteRepository navSiteRepository;
@BeforeClass
void setUp() {
NavSite navSite = new NavSite();
navSite.setSiteName("");
navSite.setSiteUrl("");
navSite.setIconPath("");
navSite.setSiteType(0);
navSite.setSort(0);
navSite.setCreateTime(new Date());
navSite.setUpdateTime(new Date());
navSiteRepository.save(navSite);
}
@Test
void findBySiteType_UnknownSiteType_ZeroSize() {
List<NavSite> navSites = navSiteRepository.findBySiteType(0);
assertThat(navSites.size()).isEqualTo(1);
}
@Test
void findBySiteType() {
List<NavSite> navSites = navSiteRepository.findBySiteType(0);
assertThat(navSites.size()).isEqualTo(1);
}
}
На данный момент мы достиглиJUnit5 + Testcontainers + LiquibaseПроведите тесты уровня DAO.
Альтернативы
Должен быть выбор между использованием встроенной базы данных, Docker и выделенной тестовой базы данных.Текущая среда непрерывной интеграции в основном имеет среду Docker.Я считаю, что тестконтейнеры будут становиться все более и более популярными, даже если они не используются в модульном тестировании.集成测试Это также очень полезный инструмент для использования в .
Обобщенно следующим образом:
- Используйте h2 или другую встроенную базу данных для тестирования уровня DAO в простых проектах.
- Выбор тестовых контейнеров в конкретном сценарии может привести к тому, что он будет соответствовать принципам AIR и работать быстро, например, хорошим выбором будет тестирование sql контроля версий в проекте liquibase.
- Вы можете использовать Testcontainers для интеграционных тестов.
напиши в конце
Сложность тестирования уровня DAO в основном заключается в удалении внешней зависимости базы данных.Уровень DAO имеет меньше кода бизнес-логики, поэтому модульный тест относительно прост в написании, в то время как код сложного сервисного уровня отличается и подвержен к юнит-тестам, которые сложно написать.В этом случае дизайн кода нужно оптимизировать.Об этой части мы расскажем в следующей статье.业务逻辑层测试подробно в.