Basic Java Tools for Building & Testing Apps
Скачать 4.76 Mb.
|
4.5. Обработка исключений И последнее, но не менее важное: интерфейс TestExecutionExceptionHandler можно использовать для определения поведения теста при обнаружении определенных типов исключений. Например, мы можем создать расширение, которое будет регистрировать и игнорировать все исключения типа FileNotFoundException , при этом перебрасывая любой другой тип: public class IgnoreFileNotFoundExceptionExtension implements TestExecutionExceptionHandler { Logger logger = LogManager .getLogger(IgnoreFileNotFoundExceptionExtension.class); @Override public void handleTestExecutionException (ExtensionContext context, Throwable throwable) throws Throwable { if (throwable instanceof FileNotFoundException) { logger.error( "File not found:" + throwable.getMessage()); return ; } throw throwable; } } 5. Регистрация расширений Теперь, когда мы определили наши тестовые расширения, нам нужно зарегистрировать их с помощью теста JUnit 5. Чтобы достичь этого, мы можем использовать аннотацию @ ExtendWith . Аннотацию можно добавить несколько раз в тест или получить список расширений в качестве параметра: @ExtendWith ({ EnvironmentExtension.class, EmployeeDatabaseSetupExtension.class, EmployeeDaoParameterResolver.class }) @ExtendWith (LoggingExtension.class) @ExtendWith (IgnoreFileNotFoundExceptionExtension.class) public class EmployeesTest { private EmployeeJdbcDao employeeDao; private Logger logger; public EmployeesTest (EmployeeJdbcDao employeeDao) { this .employeeDao = employeeDao; } @Test public void whenAddEmployee__thenGetEmployee () throws SQLException { Employee emp = new Employee( 1 , "john" ); employeeDao.add(emp); assertEquals( 1 , employeeDao.findAll().size()); } @Test public void whenGetEmployees__thenEmptyList () throws SQLException { assertEquals( 0 , employeeDao.findAll().size()); } public void setLogger (Logger logger) { this .logger = logger; } } Мы видим, что в нашем тестовом классе есть конструктор с параметром EmployeeJdbcDao , который будет разрешен путем расширения расширения EmployeeDaoParameterResolver . При добавлении EnvironmentExtension наш тест будет выполняться только в среде, отличной от "qa" В нашем тесте также будет создана таблица employees и каждый метод будет заключен в транзакцию путем добавления EmployeeDatabaseSetupExtension . Даже если сначала выполняется тест whenAddEmployee thenGetEmploee () __, который добавляет одну запись в таблицу, второй тест найдет 0 записей в таблице. Экземпляр регистратора будет добавлен в наш класс с помощью LoggingExtension . Наконец, наш тестовый класс будет игнорировать все экземпляры FileNotFoundException , так как он добавляет соответствующее расширение. 5.1. Автоматическая регистрация продления Если мы хотим зарегистрировать расширение для всех тестов в нашем приложении, мы можем сделать это, добавив полное имя в файл /META-INF/services/org.junit.jupiter.api.extension.Extension : com.baeldung.extensions.LoggingExtension Чтобы этот механизм был включен, нам также нужно установить для ключа конфигурации junit.extensions.autodetection.enabled значение true. Это можно сделать, запустив JVM со свойством – Djunit.extensions.autodetection.enabled = true или добавив параметр конфигурации в LauncherDiscoveryRequest : LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request() .selectors(selectClass( "com.baeldung.EmployeesTest" )) .configurationParameter( "junit.extensions.autodetection.enabled" , "true" ) .build(); 6. Заключение В этом руководстве мы показали, как мы можем использовать модель расширений JUnit 5 для создания пользовательских тестовых расширений Mockito и JUnit 5 - Использование Testing Mockito JUnit 5 FacebookTumblrPinterestPocketEvernoteTwitterLineEmailRedditDiggVKРесурс 1. Вступление В этой быстрой статье мы покажем , как интегрировать Mockito с моделью расширения JUnit 5 . Чтобы узнать больше о модели расширения JUnit 5, взгляните на этот article Сначала мы покажем, как создать расширение, которое автоматически создает фиктивные объекты для любого атрибута класса или параметра метода, помеченного @ Mock . Затем мы будем использовать наше расширение Mockito в тестовом классе JUnit 5. 2. Зависимости Maven 2.1. Обязательные зависимости Давайте добавим зависимости JUnit 5 (jupiter) и mockito в наш pom.xml : < dependency > < groupId >org.junit.jupiter groupId > < artifactId >junit-jupiter-engine artifactId > < version >5.3.1 version > < scope >test scope > dependency > < dependency > < groupId >org.mockito groupId > < artifactId >mockito-core artifactId > < version >2.21.0 version > < scope >test scope > dependency > Обратите внимание, что _unit-jupiter-engine - это основная библиотека JUnit 5, а junit-platform-launcher_ используется с плагином Maven и средством запуска IDE. 2.2. Плагин Surefire Давайте также сконфигурируем плагин Maven Surefire для запуска наших тестовых классов с помощью новой платформы запуска JUnit: < plugin > < artifactId >maven-surefire-plugin artifactId > < version >2.19.1 version > < dependencies > < dependency > < groupId >org.junit.platform groupId > < artifactId >junit-platform-surefire-provider artifactId > < version >1.0.1 version > dependency > dependencies > plugin > 2.3. Зависимости JUnit 4 IDE Чтобы наши тесты были совместимы с JUnit4 (vintage), для IDE, которые еще не поддерживают JUnit 5, давайте включим следующие зависимости: < dependency > < groupId >org.junit.platform groupId > < artifactId >junit-platform-runner artifactId > < version >1.2.0 version > < scope >test scope > dependency > < dependency > < groupId >org.junit.vintage groupId > < artifactId >junit-vintage-engine artifactId > < version >5.2.0 version > < scope >test scope > dependency > Кроме того, мы должны рассмотреть возможность аннотирования всех наших тестовых классов с помощью @ RunWith (JUnitPlatform.class) Последние версии junit-jupiter-engine , https://search.maven.org/classic/#search % 7Cga% 7C1% 7Cjunit% 20vintage% 20engine[junit-vintage-engine], junit-platform-launcher , и mockito-core можно загрузить из Maven Central. 3. Расширение Mockito Mockito обеспечивает реализацию расширений JUnit5 в библиотеке - https://search.maven.org/search? Q = a: mockito- junit-jupiter[mockito-junit-jupiter] Мы включим эту зависимость в наш pom.xml: < dependency > < groupId >org.mockito groupId > < artifactId >mockito-junit-jupiter artifactId > < version >2.23.0 version > < scope >test scope > dependency > 4. Создание тестового класса Давайте создадим наш тестовый класс и добавим к нему расширение Mockito: @ExtendWith (MockitoExtension.class) @RunWith (JUnitPlatform.class) public class UserServiceUnitTest { UserService userService; //} Мы можем использовать аннотацию @ Mock , чтобы добавить макет для переменной экземпляра, которую мы можем использовать в любом месте тестового класса: @Mock UserRepository userRepository; Также мы можем ввести фиктивные объекты в параметры метода: @BeforeEach void init (@Mock SettingRepository settingRepository) { userService = new DefaultUserService(userRepository, settingRepository, mailClient); Mockito.lenient().when(settingRepository.getUserMinAge()).thenReturn( 10 ); when(settingRepository.getUserNameMinLength()).thenReturn( 4 ); Mockito.lenient().when(userRepository.isUsernameAlreadyExists(any(String.class))).thenRetur n( false ); } Пожалуйста, обратите внимание на использование Mockito.lenient () здесь. Mockito создает исключение UnsupportedStubbingException, когда инициализированный макет не вызывается одним из методов теста во время выполнения. Мы можем избежать этой строгой проверки заглушки, используя этот метод при инициализации макетов. Мы можем даже вставить фиктивный объект в параметр метода тестирования: @Test void givenValidUser__whenSaveUser__thenSucceed (@Mock MailClient mailClient) { //Given user = new User( "Jerry" , 12 ); when(userRepository.insert(any(User.class))).then( new Answer 1 ; @Override public User answer (InvocationOnMock invocation) throws Throwable { User user = (User) invocation.getArgument( 0 ); user.setId(sequence++); return user; } }); userService = new DefaultUserService(userRepository, settingRepository, mailClient); //When User insertedUser = userService.register(user); //Then verify(userRepository).insert(user); Assertions.assertNotNull(user.getId()); verify(mailClient).sendUserRegistrationMail(insertedUser); } Обратите внимание, что макет MailClient , который мы вводим в качестве тестового параметра, НЕ будет тем же экземпляром, который мы добавили в метод init . 5. Заключение Junit 5 предоставил хорошую модель для расширения. Мы продемонстрировали простое расширение Mockito, которое упростило нашу логику создания макетов Conditional Test Execution The ExecutionCondition extension API in JUnit Jupiter allows developers to either enable or disable a container or test based on certain conditions programmatically. The simplest example of such a condition is the built-in DisabledCondition which supports the @Disabled annotation (see Disabling Tests ). In addition to @Disabled , JUnit Jupiter also supports several other annotation-based conditions in the org.junit.jupiter.api.condition package that allow developers to enable or disable containers and tests declaratively. When multiple ExecutionCondition extensions are registered, a container or test is disabled as soon as one of the conditions returns disabled. If you wish to provide details about why they might be disabled, every annotation associated with these built-in conditions has a disabledReason attribute available for that purpose. See ExecutionCondition and the following sections for details. Composed Annotations Note that any of the conditional annotations listed in the following sections may also be used as a meta-annotation in order to create a custom composed annotation. For example, the @TestOnMac annotation in the @EnabledOnOs demo shows how you can combine @Test and @EnabledOnOs in a single, reusable annotation. Unless otherwise stated, each of the conditional annotations listed in the following sections can only be declared once on a given test interface, test class, or test method. If a conditional annotation is directly present, indirectly present, or meta-present multiple times on a given element, only the first such annotation discovered by JUnit will be used; any additional declarations will be silently ignored. Note, however, that each conditional annotation may be used in conjunction with other conditional annotations in the org.junit.jupiter.api.condition package. 2.7.1. Operating System Conditions A container or test may be enabled or disabled on a particular operating system via the @EnabledOnOs and @DisabledOnOs annotations. @Test @EnabledOnOs ( MAC ) void onlyOnMacOs () { // ... } @TestOnMac void testOnMac () { // ... } @Test @EnabledOnOs ({ LINUX , MAC }) void onLinuxOrMac () { // ... } @Test @DisabledOnOs ( WINDOWS ) void notOnWindows () { // ... } @Target ( ElementType . METHOD ) @Retention ( RetentionPolicy . RUNTIME ) @Test @EnabledOnOs ( MAC ) @interface TestOnMac { } 2.7.2. Java Runtime Environment Conditions A container or test may be enabled or disabled on particular versions of the Java Runtime Environment (JRE) via the @EnabledOnJre and @DisabledOnJre annotations or on a particular range of versions of the JRE via the @EnabledForJreRange and @DisabledForJreRange annotations. The range defaults to JRE .JAVA_8 as the lower border ( min ) and JRE .OTHER as the higher border ( max ), which allows usage of half open ranges. @Test @EnabledOnJre ( JAVA_8 ) void onlyOnJava8 () { // ... } @Test @EnabledOnJre ({ JAVA_9 , JAVA_10 }) void onJava9Or10 () { // ... } @Test @EnabledForJreRange (min = JAVA_9 , max = JAVA_11 ) void fromJava9to11 () { // ... } @Test @EnabledForJreRange (min = JAVA_9 ) void fromJava9toCurrentJavaFeatureNumber () { // ... } @Test @EnabledForJreRange (max = JAVA_11 ) void fromJava8To11 () { // ... } @Test @DisabledOnJre ( JAVA_9 ) void notOnJava9 () { // ... } @Test @DisabledForJreRange (min = JAVA_9 , max = JAVA_11 ) void notFromJava9to11 () { // ... } @Test @DisabledForJreRange (min = JAVA_9 ) void notFromJava9toCurrentJavaFeatureNumber () { // ... } @Test @DisabledForJreRange (max = JAVA_11 ) void notFromJava8to11 () { // ... } 2.7.3. System Property Conditions A container or test may be enabled or disabled based on the value of the named JVM system property via the @EnabledIfSystemProperty and @DisabledIfSystemProperty annotations. The value supplied via the matches attribute will be interpreted as a regular expression. @Test @EnabledIfSystemProperty (named = "os.arch" , matches = ".*64.*" ) void onlyOn64BitArchitectures () { // ... } @Test @DisabledIfSystemProperty (named = "ci-server" , matches = "true" ) void notOnCiServer () { // ... } As of JUnit Jupiter 5.6, @EnabledIfSystemProperty and @DisabledIfSystemProperty are repeatable annotations. Consequently, these annotations may be declared multiple times on a test interface, test class, or test method. Specifically, these annotations will be found if they are directly present, indirectly present, or meta-present on a given element. 2.7.4. Environment Variable Conditions A container or test may be enabled or disabled based on the value of the named environment variable from the underlying operating system via the @EnabledIfEnvironmentVariable and @DisabledIfEnvironmentVariable annotations. The value supplied via the matches attribute will be interpreted as a regular expression. @Test @EnabledIfEnvironmentVariable (named = "ENV" , matches = "staging-server" ) void onlyOnStagingServer () { // ... } @Test @DisabledIfEnvironmentVariable (named = "ENV" , matches = ".*development.*" ) void notOnDeveloperWorkstation () { // ... } As of JUnit Jupiter 5.6, @EnabledIfEnvironmentVariable and @DisabledIfEnvironmentVariable are repeatable annotations. Consequently, these annotations may be declared multiple times on a test interface, test class, or test method. Specifically, these annotations will be found if they are directly present, indirectly present, or meta-present on a given element. 2.7.5. Custom Conditions A container or test may be enabled or disabled based on the boolean return of a method via the @EnabledIf and @DisabledIf annotations. The method is provided to the annotation via its name, or its fully qualified name if located outside the test class. If needed, the condition method can take a single parameter of type ExtensionContext |