1. Обзор
В этом уроке мы увидим, как параметризовать Spring integration test , реализованный в JUnit4, с помощью параметризованного JUnit test runner.
2. SpringJUnit4ClassRunner
SpringJUnit4ClassRunner – это реализация JUnit4 Class Runner , которая встраивает Spring/| TestContextManager в тест JUnit .
TestContextManager является точкой входа в фреймворк Spring TestContext и, следовательно, управляет доступом к Spring ApplicationContext и внедрением зависимостей в тестовом классе JUnit. Таким образом, SpringJUnit4ClassRunner позволяет разработчикам реализовывать интеграционные тесты для компонентов Spring, таких как контроллеры и репозитории.
Например, мы можем реализовать интеграционный тест для нашего RestController :
@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration(classes = WebConfig.class) public class RoleControllerIntegrationTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; private static final String CONTENT_TYPE = "application/text;charset=ISO-8859-1"; @Before public void setup() throws Exception { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } @Test public void givenEmployeeNameJohnWhenInvokeRoleThenReturnAdmin() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders .get("/role/John")) .andDo(print()) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().contentType(CONTENT_TYPE)) .andExpect(MockMvcResultMatchers.content().string("ADMIN")); } }
Как видно из теста, наш Контроллер принимает имя пользователя в качестве параметра пути и соответственно возвращает роль пользователя.
Теперь, чтобы протестировать эту службу REST с другой комбинацией имени пользователя/роли, нам придется реализовать новый тест:
@Test public void givenEmployeeNameDoeWhenInvokeRoleThenReturnEmployee() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders .get("/role/Doe")) .andDo(print()) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().contentType(CONTENT_TYPE)) .andExpect(MockMvcResultMatchers.content().string("EMPLOYEE")); }
Это может быстро выйти из-под контроля для сервисов, где возможно большое количество комбинаций ввода.
Чтобы избежать такого рода повторений в наших тестовых классах, давайте посмотрим, как использовать Параметризованный для реализации тестов JUnit, которые принимают несколько входных данных.
3. Использование Параметризованных
3.1. Определение параметров
Параметризованный – это пользовательский тестовый запуск JUnit, который позволяет нам написать один тестовый случай и запустить его с несколькими входными параметрами:
@RunWith(Parameterized.class) @WebAppConfiguration @ContextConfiguration(classes = WebConfig.class) public class RoleControllerParameterizedIntegrationTest { @Parameter(value = 0) public String name; @Parameter(value = 1) public String role; @Parameters public static Collection
Как показано выше, мы использовали аннотацию @Parameters для подготовки входных параметров, которые будут введены в тест JUnit. Мы также предоставили отображение этих значений в полях @Parameter | name и role.
Но теперь у нас есть еще одна проблема, которую нужно решить — JUnit не позволяет нескольким бегунам в одном тестовом классе JUnit . Это означает, что мы не можем воспользоваться SpringJUnit4ClassRunner для встраивания TestContextManager | в наш тестовый класс. Нам придется найти другой способ встраивания TestContextManager .
К счастью, весна предоставляет несколько вариантов для достижения этой цели. Мы обсудим их в следующих разделах.
3.2. Инициализация TestContextManager вручную
Первый вариант довольно прост, так как Spring позволяет нам инициализировать TestContextManager вручную:
@RunWith(Parameterized.class) @WebAppConfiguration @ContextConfiguration(classes = WebConfig.class) public class RoleControllerParameterizedIntegrationTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; private TestContextManager testContextManager; @Before public void setup() throws Exception { this.testContextManager = new TestContextManager(getClass()); this.testContextManager.prepareTestInstance(this); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } //... }
Примечательно, что в этом примере мы использовали Параметризованный runner вместо SpringJUnit4ClassRunner. Далее мы инициализировали TestContextManager в методе setup () .
Теперь мы можем реализовать наш параметризованный тест JUnit:
@Test public void givenEmployeeNameWhenInvokeRoleThenReturnRole() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders .get("/role/" + name)) .andDo(print()) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().contentType(CONTENT_TYPE)) .andExpect(MockMvcResultMatchers.content().string(role)); }
JUnit выполнит этот тестовый случай дважды — по одному разу для каждого набора входных данных, которые мы определили с помощью аннотации @Parameters .
3.3. SpringClassRule и SpringMethodRule
Как правило, не рекомендуется инициализировать TestContextManager вручную . Вместо этого Spring рекомендует использовать SpringClassRule и SpringMethodRule.
SpringClassRule реализует Junit’s TestRule — альтернативный способ написания тестовых случаев. Тестовое правило может использоваться для замены операций настройки и очистки, которые ранее выполнялись с помощью методов @Before, @BeforeClass, @After, и @AfterClass .
SpringClassRule встраивает функциональность уровня класса TestContextManager в тестовый класс JUnit. Он инициализирует TestContextManager и вызывает настройку и очистку Spring TestContext. Таким образом, он обеспечивает внедрение зависимостей и доступ к ApplicationContext .
В дополнение к SpringClassRule мы также должны использовать SpringMethodRule . который обеспечивает функциональность уровня экземпляра и уровня метода для TestContextManager.
SpringMethodRule отвечает за подготовку методов испытаний. Он также проверяет наличие тестовых случаев, помеченных для пропуска, и предотвращает их запуск.
Давайте посмотрим, как использовать этот подход в нашем тестовом классе:
@RunWith(Parameterized.class) @WebAppConfiguration @ContextConfiguration(classes = WebConfig.class) public class RoleControllerParameterizedClassRuleIntegrationTest { @ClassRule public static final SpringClassRule scr = new SpringClassRule(); @Rule public final SpringMethodRule smr = new SpringMethodRule(); @Before public void setup() throws Exception { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } //... }
4. Заключение
В этой статье мы обсудили два способа реализации интеграционных тестов Spring с использованием параметризованного тестового бегуна вместо SpringJUnit4ClassRunner . Мы видели, как инициализировать TestContextManager вручную, и мы видели пример использования SpringClassRule с SpringMethodRule , подход, рекомендованный Spring.
Хотя мы обсуждали только Параметризованный бегун в этой статье, мы действительно можем использовать любой из этих подходов с любым бегуном JUnit для написания интеграционных тестов Spring.
Как обычно, весь пример кода доступен на GitHub .