Засіб виконання з JUnit 4 у Spring
Spring TestContext Framework пропонує повну інтеграцію з JUnit 4 через спеціальний засіб виконання (runner) (підтримується на JUnit 4.12 або вище). Позначаючи тестові класи анотацією @RunWith(SpringJUnit4ClassRunner.class) або коротшим варіантом @RunWith(SpringRunner.class), розробники можуть реалізувати стандартні модульні та інтеграційні тести на основі JUnit 4 та одночасно використовувати переваги фреймворку TestContext, такі як підтримка завантаження контекстів програми, впровадження залежностей в екземпляри тестів, транзакційне виконання тестових методів тощо. Якщо тобі необхідно використовувати Spring TestContext Framework з альтернативним засобом виконання (таким як засіб виконання Parameterized з JUnit 4) або сторонніми засобами виконання (такими як MockitoJUnitRunner), ти можеш за бажанням використовувати засоби підтримки Spring для правил JUnit замість цього.
У наступному лістингу коду показані мінімальні вимоги, щоб налаштувати тестовий клас для виконання за допомогою спеціального Runner у Spring:
@RunWith(SpringRunner.class)
@TestExecutionListeners({})
public class SimpleTest {
@Test
public void testMethod() {
// логіка тестування...
}
}
@RunWith(SpringRunner::class)
@TestExecutionListeners
class SimpleTest {
@Test
fun testMethod() {
// логіка тестування...
}
}
У попередньому прикладі анотація @TestExecutionListeners налаштована з порожнім списком, щоб відключити слухачів за замовчуванням, які в іншому випадку вимагають конфігурування ApplicationContext через анотацію @ContextConfiguration.
Правила Spring JUnit 4
Пакет org.springframework.test.context.junit4.rules надає наступні правила JUnit 4 (підтримуються на JUnit 4.12 або вище):
SpringClassRuleSpringMethodRule
SpringClassRule — це TestRule з JUnit, що підтримує функції Spring TestContext Framework на рівні класів, а SpringMethodRule — це MethodRule з JUnit, що підтримує функції Spring TestContext Framework на рівні екземплярів та методів.
На відміну від SpringRunner, перевагою засобу підтримки JUnit на основі правил у Spring є те, що воно незалежно від будь-якої реалізації org.junit.runner.Runner і тому може бути поєднано з існуючими альтернативними засобами виконання (такими як Parameterized з JUnit 4) або сторонніми засобами виконання (такими як MockitoJUnitRunner).
Для підтримки повної функціональності фреймворку TestContext необхідно об'єднати SpringClassRule з SpringMethodRule. У наступному прикладі показано правильний спосіб оголошення цих правил в інтеграційному тесті:
// Опціонально вказуємо засіб виконання, що не є Runner з Spring через @RunWith(...)
@ContextConfiguration
public class IntegrationTest {
@ClassRule
public static final SpringClassRule springClassRule = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@Test
public void testMethod() {
// логіка тестування...
}
}
// Опціонально вказуємо засіб виконання, що не є Runner з Spring через @RunWith(...)
@ContextConfiguration
class IntegrationTest {
@Rule
val springMethodRule = SpringMethodRule()
@Test
fun testMethod() {
// логіка тестування...
}
companion object {
@ClassRule
val springClassRule = SpringClassRule()
}
}
Допоміжні класи JUnit 4
Пакет org.springframework.test.context.junit4 надає наступні допоміжні класи для тестових випадків на базі JUnit 4 (підтримується на JUnit 4.12 або вище):
AbstractJUnit4SpringContextTests-
AbstractTransactionalAbstractJUnit4SpringContextTests— це абстрактний базовий тестовий клас, який інтегрує Spring TestContext Framework з явною підтримкою тестуванняApplicationContextу середу JUnit 4. Якщо ти розширишAbstractJU, то можеш отримати доступ доprotectedзмінної екземпляраapplicationContext, яку можна використовувати для виконання явного пошуку бінів або для тестування стану контексту в цілому.AbstractTransactionalJUnit4SpringContextTests– це абстрактне транзакційне розширенняAbstractJUnit4SpringContextTests, яке додає деякі зручні функції для доступу до JDBC. Цей клас очікує, що вApplicationContextбуде визначено бінjavax.sql.DataSourceта бінPlatformTransactionManager. Якщо ти розширишAbstractTransactionalJUnit4SpringContextTests, то можеш отримати доступ доprotectedзмінної екземпляраjdbcTemplate, яку можна використовувати, щоб виконувати SQL-інструкції для здійснення запитів до бази даних. Можна використовувати такі запити для підтвердження стану бази даних як до, так і після виконання коду програми, пов'язаного з базою даних, а Spring гарантовано забезпечить, що такі запити будуть виконані в рамках тієї ж транзакції, що код програми. При використанні в поєднанні з інструментом ORM слід уникати хибних спрацювань. Як згадувалося в розділі Підтримка тестування JDBC",AbstractTransactionalJUnit4SpringContextTestsтакож надає допоміжні методи, які делегують повноваження методам уJdbcTestUtilsза допомогою вищезазначеногоjdbc.AbstractTransactionalJUnit4SpringContextTestsнадає методexecuteSqlScript(..)для виконання SQL-скриптів щодо налаштованогоDataSource.Ці класи є допоміжним засобом для розширення. Якщо вам не потрібно, щоб ваші тестові класи були прив'язані до ієрархії класів, специфічної для Spring, то можеш налаштувати власні спеціальні тестові класи за допомогою анотації@RunWith(SpringRunner.class)або правил JUnit для Spring.SpringExtension для JUnit Jupiter
Spring TestContext Framework пропонує повну інтеграцію з тестовим фреймворком JUnit Jupiter, наявним у JUnit 5. Анотуючи класи тестів за допомогою
@ExtendWith(SpringExtension.class), можна реалізувати стандартні модульні та інтеграційні тести на основі JUnit Jupiter та одночасно користуватися перевагами фреймворку TestContext, такими як підтримка завантаження контекстів програми, впровадження залежностей в екземпляри тестів, транзакційне виконання тестових методів і так далі. функцій, що Spring підтримує для JUnit 4 і TestNG:Використання залежностей для тестових конструкторів, тестових методів та методів зворотного виклику життєвого циклу тестів. Див. розділ "Використання залежностей за допомогою
SpringExtension"для більш детальної інформації.Повнофункціональна підтримка умовного виконання тестів на основі виразів на мові SpEL, змінних середовища, системних властивостей тощо. Докладнішу інформацію та приклади див. у документації з анотацій
@EnabledIfта@DisabledIfу розділі "Анотації з JUnit Jupiter для тестування в Spring".Спеціальні складені анотації, які об'єднують анотації зі Spring і JUnit Jupiter. Докладнішу інформацію див. у прикладах анотацій
@TransactionalDevTestConfigта@TransactionalIntegrationTestу розділі "Підтримка мета-анотацій для тестування".
У наступному лістингу коду показано, як налаштувати тестовий клас для використання
SpringExtensionу поєднанні з@ContextConfiguration:Java// Даємо команду JUnit Jupiter розширити тест засобами підтримки з Spring. @ExtendWith(SpringExtension.class) // Даємо команду Spring завантажити ApplicationContext з TestConfig.class @ContextConfiguration(classes = TestConfig.class) class SimpleTests { @Test void testMethod() { // логіка тестування... } }Kotlin// Даємо команду JUnit Jupiter розширити тест засобами підтримки з Spring. @ExtendWith(SpringExtension::class) // Даємо команду Spring завантажити ApplicationContext з TestConfig.class @ContextConfiguration(classes = [TestConfig::class]) class SimpleTests { @Test fun testMethod() { // логіка тестування... } }Оскільки в JUnit 5 можна також використовувати анотації як мета-анотації, Spring надає складові анотації
@SpringJUnitConfigта@SpringJUnitWebConfigдля спрощення конфігурації тестовогоApplicationContextта JUnit Jupiter.У наступному прикладі анотація
@SpringJUnitConfigвикористовується для зменшення обсягу конфігурації, використаної в попередній приклад:Java// Дає Spring команду зареєструвати SpringExtension в JUnit // Jupiter і завантажити ApplicationContext з TestConfig.class @SpringJUnitConfig(TestConfig.class) class SimpleTests { @Test void testMethod() { // логіка тестування... } }Kotlin// Дає Spring команду зареєструвати SpringExtension в JUnit // Jupiter і завантажити ApplicationContext з TestConfig.class @SpringJUnitConfig(TestConfig::class) class SimpleTests { @Test fun testMethod() { // логіка тестування... } }Аналогічно, в наступному прикладі анотація
@SpringJUnitWebConfigвикористана, щоб створитиWebApplicationContextдля використання з JUnit Jupiter:Java// Дає Spring команду зареєструвати SpringExtension в JUnit // Jupiter і завантажити WebApplicationContext з TestWebConfig.class @SpringJUnitWebConfig(TestWebConfig.class) class SimpleWebTests { @Test void testMethod() { // логіка тестування... } }Kotlin// Дає Spring команду зареєструвати SpringExtension в JUnit // Jupiter і завантажити WebApplicationContext from TestWebConfig::class @SpringJUnitWebConfig(TestWebConfig::class) class SimpleWebTests { @Test fun testMethod() { // логіка тестування... } }Детальнішу інформацію див. у документації
@SpringJUnitConfigта@SpringJUnitWebConfigу розділі "Анотації з JUnit Jupiter для тестування в Spring".Використання залежностей за допомогою
SpringExtensionSpringExtensionреалізує API-інтерфейс розширенняParameterResolverз JUnit Jupiter, що дозволяє Spring забезпечити впровадження залежностей для тестових конструкторів, тестових методів та методів зворотного виклику життєвого циклу тестів.Зокрема,
SpringExtensionможе впроваджувати залежності зApplicationContextтесту в тестові конструктори та методи, анотовані@BeforeAll,@AfterAll,@BeforeEach,@AfterEach,@Test,@RepeatedTest,@ParameterizedTestта іншими анотаціями.Впровадження залежностей через конструктор
Якщо певний параметр у конструкторі тестового класу з JUnit Jupiter має тип
ApplicationContext(або його підтип) або анотований чи мета-анотований за допомогою@Autowired,@Qualifierчи@Value, Spring впроваджує значення цього параметра з використанням відповідного біна або значення зApplicationContextтесту.Spring також можна налаштувати на автоматичне виявлення та зв'язування всіх аргументів конструктора тестового класу, якщо конструктор вважається автоматично зв'язуваним. Конструктор вважається таким, що автоматично зв'язується, якщо виконується одна з наступних умов (у порядку старшинства).
Конструктор анотований
@Autowired.Анотація @TestConstructorприсутня або мета-присутня для тестового класу з атрибутомautowireMode,встановленим уALL.Режим автоматичного виявлення та зв'язування тестового конструктора за замовчуванням було змінено на
ALL.
Докладніше про використання анотації
@TestConstructorта про те, як змінити режим автоматичного виявлення та зв'язування глобального тестового конструктора, див. у розділі, присвяченому анотації@TestConstructor.Якщо конструктор для тестового класу вважається автоматично зв'язуваним, Spring бере на себе дозвіл аргументів для всіх параметрів у конструкторі. Отже, жодний іншийParameterResolver, зареєстрований у JUnit Jupiter, не може дозволяти параметри для такого конструктора.Впровадження залежностей через конструктор для тестових класів не можна використовувати в поєднанні із засобами підтримки анотації
@TestInstance(PER_CLASS)з JUnit Jupiter, якщо анотація@DirtiesContextвикористовується для закриттяApplicationContextтесту перед або після тестових методів.Причина в тому, що анотація
@TestInstance(PER_CLASS)дає JUnit Jupiter команду кешувати екземпляр тесту між викликами тестового методу. Отже, тестовий екземпляр зберігатиме посилання на біни, спочатку впроваджені зApplicationContext, який згодом був закритий. Оскільки конструктор для тестового класу в таких сценаріях буде викликаний лише один раз, впровадження залежностей не повториться, і наступні тести взаємодіятимуть із бінами із закритогоApplicationContext, що може призвести до помилок.Щоб використовувати анотацію
@DirtiesContextу режимі "перед тестовим методом" або "після тестового методу" у поєднанні з анотацією@TestInstance(PER_CLASS), необхідно налаштувати залежності від Spring на їх отримання шляхом впровадження через поле або сеттер, щоб їх можна було повторно впроваджувати між викликами тестового методу.У наступному прикладі Spring впроваджує бін OrderService з
ApplicationContext, завантаженого зTestConfig.class, в конструкторOrderServiceIntegrationTests.Java@SpringJUnitConfig(TestConfig .class) class OrderServiceIntegrationTests { private final OrderService orderService; @Autowired OrderServiceIntegrationTests(OrderService orderService) { this.orderService = orderService; } // тести, які використовують впроваджену службу OrderService }Kotlin@SpringJUnitConfig(TestConfig::class) class OrderServiceIntegrationTests @Autowired constructor(private val orderService: OrderService){ // тести, які використовують впроваджену службу OrderService }Зверни увагу, що ця функція дозволяє тестовим залежностям бути
finalі, отже, незмінними.Якщо властивість
spring.test.constructor.autowire.modeмає значенняall(див.@TestConstructor), можна пропустити оголошення анотації@Autowiredдля конструктора з попереднього прикладу, в результаті чого отримаємо наступне.Java@SpringJUnitConfig(TestConfig.class) class OrderServiceIntegrationTests { private final OrderService orderService; OrderServiceIntegrationTests(OrderService orderService) { this.orderService = orderService; } // тести, які використовують впроваджену службу OrderService }Kotlin@SpringJUnitConfig(TestConfig::class) class OrderServiceIntegrationTests(val orderService:OrderService) { // тести, які використовують впроваджену службу OrderService }Впровадження залежностей через метод
Якщо параметр тестового методу з JUnit Jupiter або методу зворотного виклику життєвого циклу тесту має тип
ApplicationContext(чи його підтип) або анотований чи мета-анотований за допомогою анотацій@Autowired,@Qualifierабо@Value, Spring впроваджує значення для цього конкретного параметра з використанням відповідного бына зApplicationContextтесту.У наступному прикладі Spring впроваджує
OrderServiceзApplicationContext, завантаженого зTestConfig.class, до тестового методуdeleteOrder():Java@SpringJUnitConfig(TestConfig.class) class OrderServiceIntegrationTests { @Test void deleteOrder(@Autowired OrderService orderService) { // використовуємо orderService з ApplicationContext тесту } }Kotlin@SpringJUnitConfig(TestConfig::class) class OrderServiceIntegrationTests { @Test fun deleteOrder(@Autowired orderService: OrderService) { // використовуємо orderService з ApplicationContext тесту } }Завдяки надійності підтримки
ParameterResolverв JUnit Jupiter, до одного методу можна впровадити кілька залежностей, причому не тільки зі Spring, а й із самого JUnit Jupiter або інших сторонніх розширень.У наступному прикладі показано, як зробити так, щоб Spring та JUnit Jupiter одночасно впроваджували залежності до тестового методу
placeOrderRepeatedly().Java@SpringJUnitConfig(TestConfig.class) class OrderServiceIntegrationTests { @RepeatedTest(10) void placeOrderRepeatedly(RepetitionInfo repeatedInfo, @Autowired OrderService orderService) { // використовуємо orderService з ApplicationContext тесту // та repetitionInfo з JUnit Jupiter } }Kotlin@SpringJUnitConfig(TestConfig::class) class OrderServiceIntegrationTests { @RepeatedTest (10) fun placeOrderRepeatedly(repetitionInfo:RepetitionInfo, @Autowired orderService:OrderService) { // використовуємо orderService з ApplicationContext тесту // та repetitionInfo з JUnit Jupiter } }Зверни увагу, що використання анотації
@RepeatedTestз JUnit Jupiter дозволяє тестовому методу отримувати доступ доRepetitionInfo.Конфігурація тестового класу з анотацією
@NestedSpring TestContext Framework підтримує використання пов'язаних з тестами анотацій для тестових класів, позначених анотацією
@Nested, в JUnit Jupiter, починаючи з версії Spring Framework 5.0; однак до Spring Framework 5.3 конфігураційні анотації тестів на рівні класів не успадковувалися від вкладених класів, як це відбувається з суперкласами. У Spring Framework 5.3 з'явилася повнофункціональна підтримка успадкування конфігурації тестового класу від вкладених класів, і така конфігурація успадковуватиметься за замовчуванням. Щоб змінити режимINHERITза замовчуванням на режимOVERRIDE, можна позначити окремий тестовий клас, анотований@Nested, анотацією@NestedTestConfiguration(EnclosingConfiguration.OVERRIDE). Явне оголошення@NestedTestConfigurationзастосовуватиметься до анотованого тестового класу, а також до всіх його підкласів та вкладених класів. Таким чином, можна анотувати високорівневий тестовий клас за допомогою анотації@NestedTestConfiguration, і це буде застосовано до всіх його вкладених тестових класів рекурсивно.Для того, щоб команди розробників могли змінити значення за замовчуванням на
Хоча наступний приклад Hello World дуже спрощений, він показує, як оголосити конфігурацію для високорівневого класу, яка успадковується його тестовими класами, позначеними анотацієюOVERRIDE— наприклад, для забезпечення сумісності з версіями Spring Framework 5.0-5.2 – режим за замовчуванням можна змінити глобально через системну властивість JVM або файлspring.propertiesв корені шляху класів. Докладніші відомості див. у статті "Зміна режиму наслідування об'ємної конфігурації за замовчуванням".@Nested. У цьому прикладі успадковується лише конфігураційний класTestConfig. Кожен вкладений тестовий клас надає свій власний набір активних профілів, внаслідок чого для кожного вкладеного тестового класу створюється окремийApplicationContext(докладніше див. розділ "Кешування контексту"). Зверніться до списку підтримуваних анотацій, щоб дізнатися, які анотації можуть бути успадковані в тестових класах з анотацією@Nested.Java@SpringJUnitConfig(TestConfig.class) class GreetingServiceTests { @Nested @ActiveProfiles("lang_en") class EnglishGreetings { @Test void hello(@Autowired GreetingService service) { assertThat(service.greetWorld()).isEqualTo("Hello World"); } } @Nested @ActiveProfiles("lang_de") class GermanGreetings { @Test void hello(@Autowired GreetingService service) { assertThat(service.greetWorld()).isEqualTo("Hallo Welt"); } } }Kotlin@SpringJUnitConfig(TestConfig::class) class GreetingServiceTests { @Nested @ActiveProfiles("lang_en") inner class EnglishGreetings { @Test fun hello(@Autowired service:GreetingService) { assertThat(service.greetWorld()).isEqualTo("Hello World") } } @Nested @ActiveProfiles("lang_de") inner class GermanGreetings { @Test fun hello(@Autowired service:GreetingService) { assertThat(service.greetWorld()).isEqualTo("Hallo Welt") } } }Допоміжні класи TestNG
Пакет
org.springframework.test.context.testngнадає такі допоміжні класи для тестування на основі TestNG:AbstractTestNGSpringContextTestsAbstractTransactionalTestNGSpringContextTests
AbstractTestNGS SpringContextTests— це абстрактний базовий тестовий клас, який інтегрує Spring TestContext Framework з явною підтримкою тестування ApplicationContext до середовища TestNG. Якщо ти розшириш AbstractTestNGSpringContextTests, можеш отримати доступ до protected змінної екземпляра applicationContext, яку можна використовувати для виконання явного пошуку БІНIВ або для тестування стану контексту в цілому.AbstractTransactionalTestNGSpringContextTestsрозширенняAbstractTestNGSpringContextTests, яке додає деякі зручні функції для доступу до JDBC. Цей клас очікує, що вApplicationContextбуде визначено бінjavax.sql.DataSourceта бінPlatformTransactionManager. Якщо ти розширишAbstractTransactionalTestNGSpringContextTests, можна отримати доступ доprotectedзмінної екземпляраjdbcTemplate, яку можна використовувати, щоб виконувати SQL-інструкції для здійснення запитів до бази даних. Можна використовувати такі запити для підтвердження стану бази даних як до, так і після виконання коду програми, пов'язаного з базою даних, а Spring гарантовано забезпечить, що такі запити будуть виконані в рамках тієї ж транзакції, що код програми. При використанні в поєднанні з інструментом ORM слід уникати хибних спрацювань. Як згадувалося в розділі "Підтримка тестування JDBC",AbstractTransactionalTestNGSpringContextTestsтакож надає допоміжні методи, які делегують повноваження методамJdbcTestUtilsза допомогою вищезазначеногоjdbcДо того ж,AbstractTransactionalTestNGSpringContextTestsнадає методexecuteSqlScript(..)для виконання SQL-скриптів щодо налаштованогоDataSource.Ці класи є допоміжним засобом для розширення. Якщо тобі не потрібно, щоб тестові класи були прив'язані до ієрархії класів, специфічної для Spring, то можеш налаштувати свої власні спеціальні тестові класи за допомогою анотацій@ContextConfiguration,@TestExecutionListenersтощо, а також вручну інструментувати свій тестовий клас за допомогоюTestContextManager. Дивися вихідний кодAbstractTestNGSpringContextTestsяк приклад того, як можна інструментувати твій тестовий клас.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ