Статьи

Распределите тесты по категориям, чтобы сократить время сборки.


Прежде чем мы перейдем к основному содержанию статьи, давайте разберемся с некоторыми определениями.

Модульные тесты

Модульные тесты — это небольшие тесты (тесты одного варианта использования или модуля), выполняемые в памяти (не взаимодействуют с базой данных, очередями сообщений и т. Д.), Повторяемые и быстрые. Для нашего разговора давайте ограничимся тестовыми примерами на основе JUnit, которые разработчики пишут для проверки своего отдельного фрагмента кода.

Интеграционные тесты

Интеграционные тесты больше (тестируют один поток или интеграцию компонентов), не обязательно работают только в памяти (взаимодействуют с базой данных, файловыми системами, очередями сообщений и т. Д.), Определенно медленнее и не обязательно повторяются (так как в результате может произойти изменение например, в случае внесения изменений в базу данных).

Почему эта дифференциация важна?

В гибком программировании одна из основных концепций — запускать модульные тесты время от времени (несколько раз в день на блоках разработчика) и принудительно запускать интеграционные тесты один раз в день (на сервере непрерывной интеграции, а не на блоках разработчика). , Обратите внимание, что разработчик должен иметь возможность запускать интеграционные тесты, когда он этого хочет, просто он отделен от модульных тестов, поэтому у разработчика теперь есть возможность не запускать интеграционные тесты каждый раз, когда он хочет запускать тесты.

Как именно эта гибкость помогает?

  1. Разработчики строят чаще. Это — в Agile мире означает — разработчики запускают модульные тесты чаще (часто несколько раз в день).
  2. Разработчики быстрее узнают об ошибке и тратят меньше времени на кодирование сломанной кодовой базы. Это означает экономию времени и денег.
  3. Исправлять ошибки проще и быстрее. Учитывая частоту сборок, можно было бы зафиксировать меньшее количество «нарушающего кода», и, следовательно, легче обнулить ошибку и исправить ее.
  4. И последнее, но не менее важное: любой, кто занимался профессиональным программированием, будет свидетельствовать о том, что, хотя он помогает делать 10-минутный перерыв один раз в день, ничто не убивает креативность кодера более эффективно, чем необходимость ждать 1 час сборки. Воздействие на мораль нематериально, но огромно.

Как именно сбить время сборки?

Нет единого размера, который подходит всем (никогда не бывает). Точные выполняемые шаги для сокращения времени сборки и выпуска будут зависеть от многих переменных, включая технологический стек продукта (Java, DotNet, php), технологии сборки и выпуска (Batch-файлы, Ant, Maven) и многие другие.

Для комбинации Java, Maven и JUnit …

Давайте начнем с использования Maven для создания простого Java-приложения для демонстрации случая.

\ MavenCommands.bat

ECHO OFF 

REM =============================
REM Set the env. variables. 
REM =============================
SET PATH=%PATH%;C:\ProgramFiles\apache-maven-3.0.3\bin;
SET JAVA_HOME=C:\ProgramFiles\Java\jdk1.7.0

REM =============================
REM Create a simple java application. 
REM =============================
call mvn archetype:create ^
  -DarchetypeGroupId=org.apache.maven.archetypes ^
  -DgroupId=org.academy ^
  -DartifactId=app001
pause

Если вы запустите этот пакетный файл, вы начнете со стандартного готового приложения Java.

Java-приложение по умолчанию не поставляется с последней версией JUnit. Возможно, вы захотите изменить конфигурацию Maven, чтобы добавить последнюю версию JUnit.

\ pom.xml

[...]
<junit.version>4.10</junit.version>
[...]
<!-- Unit test. -->                    
<dependency>                           
<groupId>junit</groupId>           
<artifactId>junit</artifactId>     
<version>${junit.version}</version>
<scope>test</scope>                
</dependency>    

Теперь добавьте тестовый класс JUnit.

/app001/src/test/java/org/academy/AppTest.java

public class AppTest {

private final static Logger logger = LoggerFactory.getLogger(AppTest.class);

@Test
public void smallAndFastUnitTest() {
 logger.debug("Quick unit test. It is not expected to interact with DB etc.");
 assertTrue(true);
}

@Test
@Category(IntegrationTest.class)
public void longAndSlowIntegrationTest() {
 logger.debug("Time consuming integration test. It is expected to interact with DB etc.");
 assertTrue(true);
}
}

Как вы могли заметить, есть маркер IntegrationTest.class. Вам также нужно будет создать этот класс.

/app001/src/test/java/org/academy/annotation/type/IntegrationTest.java

public interface IntegrationTest {
  // Just a marker interface. 
}

Создание интерфейса маркера и аннотирование ваших тестовых методов (или классов, если вы захотите) — это все, что вам нужно сделать в своем коде.

Теперь все, что остается сделать, это сказать maven запускать «интеграционные тесты» только на этапе интеграционных тестов. Это означает, что разработчик может в большинстве случаев запускать только модульные тесты (быстрые, которые изолированы от баз данных, очередей и т. Д.). Сервер Continuous Integration, т.е. Hudson (или ему подобные), будет запускать модульные тесты и интеграционные тесты (которые будут медленнее, так как ожидается, что они будут взаимодействовать с базами данных и т. Д.), И это может произойти в одночасье.

Итак, вот как вы это делаете.

/pom.xml

<!-- Running unit tests. -->                                                        
<plugin>                                                                            
<groupId>org.apache.maven.plugins</groupId>                                     
<artifactId>maven-surefire-plugin</artifactId>                                  
<version>2.12</version>                                                         
<dependencies>                                                                  
<dependency>                                                                
<groupId>org.apache.maven.surefire</groupId>                            
<artifactId>surefire-junit47</artifactId>                               
<version>2.12</version>                                                 
</dependency>                                                               
</dependencies>                                                                 
<configuration>                                                                 
<argLine>-XX:-UseSplitVerifier</argLine>                                
<excludedGroups>org.academy.annotation.type.IntegrationTest</excludedGroups>
</configuration>                                                                
</plugin>         

 

Это будет означать, что разработчик может запустить все модульные тесты, просто используя один вкладыш.

mvn clean test

Это не будет запускать любой тест, аннотированный как интеграционный тест.

Для тестирования интеграции добавьте следующее.

/pom.xml

<!-- Running integration tests. -->                                 
<plugin>                                                            
<artifactId>maven-failsafe-plugin</artifactId>                  
<version>2.12</version>                                         
<dependencies>                                                  
<dependency>                                                
<groupId>org.apache.maven.surefire</groupId>            
<artifactId>surefire-junit47</artifactId>               
<version>2.12</version>                                 
</dependency>                                               
</dependencies>                                                 
<configuration>                                                 
<groups>org.academy.annotation.type.IntegrationTest</groups>
</configuration>                                                
<executions>                                                    
<execution>                                                 
<goals>                                                 
<goal>integration-test</goal>                       
</goals>                                                
<configuration>                                         
<includes>                                          
<include>**/*.class</include>                   
</includes>                                         
</configuration>                                        
</execution>                                                
</executions>                                                   
</plugin>  

Это означает, что Hudson или разработчик (если он выберет) может запустить все тесты, модуль и интеграцию одной командой.

mvn clean verify    

Конечно, если вы решите пройти весь путь: компиляция, запуск модульных тестов, пакетирование, запуск интеграционных тестов и развертывание, вы также можете сделать это с помощью одной командной строки.

mvn clean install

Вот и все. Вы сделали один шаг к более быстрой сборке и более гибкому способу работы. Удачного кодирования.

Пожалуйста, обратите внимание:  статья была воспроизведена из моей оригинальной записи в блоге здесь