1. Обзор
В этом руководстве мы увидим, как параметризовать интеграционный тест Spring, реализованный в JUnit4, с помощью средства запуска тестов Parameterized
JUnit.
2. SpringJUnit4ClassRunner
SpringJUnit4ClassRunner
— это реализация ClassRunner JUnit4
, которая встраивает TestContextManager
Spring в тест 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"));
}
Это может быстро выйти из-под контроля для сервисов, где возможно большое количество комбинаций ввода.
Чтобы избежать такого повторения в наших тестовых классах, давайте посмотрим, как использовать Parameterized
для реализации тестов JUnit, которые принимают несколько входных данных.
3. Использование параметризованного
3.1. Определение параметров
Parameterized
— это специальный инструмент запуска тестов 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<Object[]> data() {
Collection<Object[]> params = new ArrayList();
params.add(new Object[]{"John", "ADMIN"});
params.add(new Object[]{"Doe", "EMPLOYEE"});
return params;
}
//...
}
Как показано выше, мы использовали аннотацию @Parameters
для подготовки входных параметров для ввода в тест JUnit. Мы также предусмотрели сопоставление этих значений в имени поля
@Parameter и
роли .
Но теперь нам нужно решить другую проблему — JUnit не допускает нескольких исполнителей в одном тестовом классе JUnit . Это означает , что мы не можем использовать преимущества SpringJUnit4ClassRunner
для встраивания TestContextManager
в наш тестовый класс. Нам придется найти другой способ встраивания TestContextManager
.
К счастью, Spring предоставляет несколько вариантов для достижения этой цели. Мы обсудим их в следующих разделах.
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();
}
//...
}
Примечательно, что в этом примере мы использовали Parameterized
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
реализует TestRule JUnit —
альтернативный способ написания тестовых случаев. 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 с использованием Parameterized
test runner вместо SpringJUnit4ClassRunner
. Мы увидели, как инициализировать TestContextManager
вручную, и мы увидели пример использования SpringClassRule
с SpringMethodRule
— подход, рекомендованный Spring.
Хотя в этой статье мы обсуждали только параметризованный бегун, на
самом деле мы можем использовать любой из этих подходов с любым исполнителем JUnit для написания интеграционных тестов Spring.
Как обычно, весь код примера доступен на GitHub .