Usando SpringJUnit4ClassRunner com Parameterized

Usando SpringJUnit4ClassRunner com Parameterized

1. Visão geral

Neste tutorial, veremos como parametrizar umSpring integration test implementado em JUnit4 com um executor de teste JUnitParameterized.

2. SpringJUnit4ClassRunner

SpringJUnit4ClassRunner  é uma implementação deClassRunner thatembeds Spring’s TestContextManager into a JUnit test da JUnit4.

TestContextManager  é o ponto de entrada no framework SpringTestContext e, portanto, gerencia o acesso à injeção de dependência de areia do SpringApplicationContext em uma classe de teste JUnit. Portanto,SpringJUnit4ClassRunner permite que os desenvolvedores implementem testes de integração para componentes Spring, como controladores e repositórios.

Por exemplo, podemos implementar um teste de integração para nossoRestController:

@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"));
    }
}

Como pode ser visto no teste, nossoController aceita um nome de usuário como um parâmetro de caminho e retorna a função do usuário de acordo.

Agora,in order to test this REST service with a different user name/role combination, we would have to implement a new test:

@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"));
}

Estecan quickly get out of hand for services where a large number of input combinations are possible.

Para evitar esse tipo de repetição em nossas classes de teste, vamos ver como usarParameterized  para implementar testes JUnit que aceitam várias entradas.

3. UsandoParameterized

3.1. Definindo parâmetros

Parameterized é um executor de teste JUnit personalizado que nos permite escrever um único caso de teste e executá-lo em vários parâmetros de entrada:

@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 data() {
        Collection params = new ArrayList();
        params.add(new Object[]{"John", "ADMIN"});
        params.add(new Object[]{"Doe", "EMPLOYEE"});

        return params;
    }

    //...
}

Conforme mostrado acima, usamos a anotação@Parameters para preparar os parâmetros de entrada a serem injetados no teste JUnit. Também fornecemos o mapeamento desses valores em@Parameter fieldsname androle.

Mas agora, temos outro problema para resolver -JUnit doesn’t allow multiple runners in one JUnit test class. Isso significawe can’t take advantage of SpringJUnit4ClassRunner to embed the *TestContextManager* into nossa classe de teste. Teremos que encontrar outra maneira de incorporarTestContextManager.

Felizmente, o Spring oferece algumas opções para conseguir isso. Discutiremos isso nas seções a seguir.

3.2. Inicializando oTestContextManager manualmente

A primeira opção é bastante simples, pois o Spring nos permite inicializarTestContextManager manualmente:

@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();
    }

    //...
}

Notavelmente, neste exemplo, usamos oParameterized runner em vez deSpringJUnit4ClassRunner. Em seguida, inicializamos o métodoTestContextManager em o métodosetup().

Agora, podemos implementar nosso teste JUnit parametrizado:

@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 will execute this test case twice - uma vez para cada conjunto de entradas que definimos usando a anotação@Parameters.

3.3. SpringClassRule eSpringMethodRule

Geralmente,it is not recommended to initialize the TestContextManager manually. Em vez disso, Spring recomenda usarSpringClassRuleeSpringMethodRule.

SpringClassRule implementaTestRule — do JUnit como uma maneira alternativa de escrever casos de teste. TestRule pode ser usado para substituir as operações de configuração e limpeza que foram feitas anteriormente com métodos@Before, @BeforeClass, @After,e@AfterClass .

SpringClassRule incorpora a funcionalidade de nível de classe deTestContextManager em uma classe de teste JUnit. Ele inicializa a areiaTestContextManager chama a configuração e limpeza do SpringTestContext.. Portanto, ele fornece injeção de dependência e acesso aApplicationContext.

Além deSpringClassRule, também devemos usarSpringMethodRule. que fornece a funcionalidade de nível de instância e nível de método paraTestContextManager.

SpringMethodRule  é responsável pela preparação dos métodos de ensaio. Ele também verifica casos de teste marcados para serem ignorados e impede a execução deles.

Vamos ver como usar essa abordagem em nossa aula de teste:

@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. Conclusão

Neste artigo, discutimos duas maneiras de implementar testes de integração Spring usando o executor de testeParameterized em vez deSpringJUnit4ClassRunner. Vimos como inicializarTestContextManager manualmente e vimos um exemplo usandoSpringClassRule comSpringMethodRule, a abordagem recomendada pelo Spring.

Embora nós discutimos apenas oParameterized runner neste artigo,we can actually use either of these approaches with any JUnit runner para escrever testes de integração Spring.

Como de costume, todo o código de exemplo está disponível emGitHub.