Introdução ao JUnitParams
1. Visão geral
Neste artigo, exploraremos a bibliotecaJUnitParams e seus usos. Simplificando, essa biblioteca fornece fácil parametrização de métodos de teste em testesJUnit.
Há situações em que a única coisa que muda entre vários testes são os parâmetros. O próprioJUnit tem suporte para parametrização eJUnitParams melhora significativamente essa funcionalidade.
2. Dependência do Maven
Para usarJUnitParams em nosso projeto, precisamos adicioná-lo ao nossopom.xml:
pl.pragmatists
JUnitParams
1.1.0
A versão mais recente da biblioteca pode ser encontradahere.
3. Cenário de teste
Vamos criar uma classe que faz a adição segura de dois inteiros. Isso deve retornarInteger.MAX_VALUE se estourar eInteger.MIN_VALUE se estourar:
public class SafeAdditionUtil {
public int safeAdd(int a, int b) {
long result = ((long) a) + b;
if (result > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
} else if (result < Integer.MIN_VALUE) {
return Integer.MIN_VALUE;
}
return (int) result;
}
}
4. Construindo um Método de Teste Simples
Precisamos testar a implementação do método para diferentes combinações de valores de entrada, para garantir que a implementação seja verdadeira para todos os cenários possíveis. JUnitParams fornece mais de uma maneira de obter a criação do teste parametrizado.
Vamos usar a abordagem básica com um mínimo de codificação e ver como isso é feito. Depois disso, podemos ver quais as outras maneiras possíveis de implementar os cenários de teste usando JUnitParams são:
@RunWith(JUnitParamsRunner.class)
public class SafeAdditionUtilTest {
private SafeAdditionUtil serviceUnderTest
= new SafeAdditionUtil();
@Test
@Parameters({
"1, 2, 3",
"-10, 30, 20",
"15, -5, 10",
"-5, -10, -15" })
public void whenWithAnnotationProvidedParams_thenSafeAdd(
int a, int b, int expectedValue) {
assertEquals(expectedValue, serviceUnderTest.safeAdd(a, b));
}
}
Agora vamos ver como essa classe de teste difere de uma classe de testeJUnit normal.
A primeira coisa que notamos é quethere is adifferent test runner na anotação de classe -JUnitParamsRunner.
Passando para o método de teste, vemos que o método de teste é anotado com a anotação@Parameters com uma matriz de parâmetros de entrada. Indica diferentes cenários de teste que serão usados para testar nosso método de serviço.
Se executarmos o teste usando Maven, veremos quewe are running four test cases and not a single one. A saída seria semelhante à seguinte:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.example.junitparams.SafeAdditionUtilTest
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.068 sec
- in com.example.junitparams.SafeAdditionUtilTest
Results :
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
5. Diferentes tipos de parametrização de métodos de teste
O fornecimento de parâmetros de teste diretamente na anotação certamente não é a maneira mais legível se tivermos muitos cenários possíveis que precisam ser testados. JUnitParams oferece um conjunto de abordagens diferentes que podemos utilizar para criar os testes parametrizados:
-
Diretamente na anotação@Parameters (usado no exemplo acima)
-
Usando um método de teste nomeado definido na anotação
-
Usando um método mapeado pelo nome do método de teste
-
Uma classe de teste nomeada definida na anotação
-
Usando um arquivo CSV
Vamos explorar as abordagens uma por uma.
5.1. Diretamente na anotação@Parameters
Já usamos essa abordagem no exemplo que tentamos. O que precisamos ter em mente é que devemos fornecer uma matriz de sequências de parâmetros. Dentro da cadeia de parâmetros, cada parâmetro é separado por vírgula.
Por exemplo, a matriz teria a forma de\{ “1, 2, 3”, “-10, 30, 20”}e um conjunto de parâmetros é representado como“1, 2, 3”.
A limitação dessa abordagem é que só podemos fornecer primitivas eStrings como parâmetros de teste. Não é possível enviar objetos como parâmetros do método de teste também.
5.2. Método de Parâmetro
Podemos fornecer os parâmetros do método de teste usando outro método dentro da classe. Vamos ver um exemplo primeiro:
@Test
@Parameters(method = "parametersToTestAdd")
public void whenWithNamedMethod_thenSafeAdd(
int a, int b, int expectedValue) {
assertEquals(expectedValue, serviceUnderTest.safeAdd(a, b));
}
private Object[] parametersToTestAdd() {
return new Object[] {
new Object[] { 1, 2, 3 },
new Object[] { -10, 30, 20 },
new Object[] { Integer.MAX_VALUE, 2, Integer.MAX_VALUE },
new Object[] { Integer.MIN_VALUE, -8, Integer.MIN_VALUE }
};
}
O método de teste é anotado em relação ao métodoparametersToAdd(),e busca os parâmetros executando o método referenciado.
A especificação do método do provedor deve retornar uma matriz deObjects como resultado. Se um método com o nome fornecido não estiver disponível, o caso de teste falhará com o erro:
java.lang.RuntimeException: Could not find method: bogusMethodName so no params were used.
5.3. Método mapeado pelo nome do método de teste
Se não especificarmos nada na anotação@Parameters,JUnitParams tenta carregar um método de provedor de dados de teste com base no nome do método de teste. O nome do método é construído como“parametersFor”+ <test method name>:
@Test
@Parameters
public void whenWithnoParam_thenLoadByNameSafeAdd(
int a, int b, int expectedValue) {
assertEquals(expectedValue, serviceUnderTest.safeAdd(a, b));
}
private Object[] parametersForWhenWithnoParam_thenLoadByNameSafe() {
return new Object[] {
new Object[] { 1, 2, 3 },
new Object[] { -10, 30, 20 },
new Object[] { Integer.MAX_VALUE, 2, Integer.MAX_VALUE },
new Object[] { Integer.MIN_VALUE, -8, Integer.MIN_VALUE }
};
}
No exemplo acima, o nome do método de teste éwhenWithnoParam_shouldLoadByNameAbdSafeAdd().
Portanto, quando o método de teste está sendo executado, ele procura um método de provedor de dados com o nomeparametersForWhenWithnoParam_shouldLoadByNameAbdSafeAdd().
Como esse método existe, ele carregará dados e executará o teste. If there is no such method matching the required name, the test fails como no exemplo acima.
5.4. Classe de teste nomeada definida na anotação
Semelhante à maneira como nos referimos a um método de provedor de dados em um exemplo anterior, podemos nos referir a uma classe separada para fornecer os dados para o nosso teste:
@Test
@Parameters(source = TestDataProvider.class)
public void whenWithNamedClass_thenSafeAdd(
int a, int b, int expectedValue) {
assertEquals(expectedValue, serviceUnderTest.safeAdd(a, b));
}
public class TestDataProvider {
public static Object[] provideBasicData() {
return new Object[] {
new Object[] { 1, 2, 3 },
new Object[] { -10, 30, 20 },
new Object[] { 15, -5, 10 },
new Object[] { -5, -10, -15 }
};
}
public static Object[] provideEdgeCaseData() {
return new Object[] {
new Object[] {
Integer.MAX_VALUE, 2, Integer.MAX_VALUE },
new Object[] {
Integer.MIN_VALUE, -2, Integer.MIN_VALUE },
};
}
}
Podemos ter qualquer número de provedores de dados de teste em uma classe, uma vez que o nome do método começa com "fornecer". Nesse caso, o executor escolhe esses métodos e retorna os dados.
Se nenhum método de classe estiver atendendo a esse requisito, mesmo que esses métodos retornem uma matriz deObjects, esses métodos serão ignorados.
5.5. Usando um arquivo CSV
Podemos usar um arquivo CSV externo para carregar os dados de teste. Isso ajuda se o número de possíveis casos de teste for bastante significativo ou se os casos de teste forem alterados frequentemente. As alterações podem ser feitas sem afetar o código de teste.
Digamos que temos um arquivo CSV com parâmetros de teste comoJunitParamsTestParameters.csv:
1,2,3
-10, 30, 20
15, -5, 10
-5, -10, -15
Agora vamos ver comothis file can be used to load test parameters no método de teste:
@Test
@FileParameters("src/test/resources/JunitParamsTestParameters.csv")
public void whenWithCsvFile_thenSafeAdd(
int a, int b, int expectedValue) {
assertEquals(expectedValue, serviceUnderTest.safeAdd(a, b));
}
Uma limitação dessa abordagem é que não é possível transmitir objetos complexos. Apenas primitivas eStrings são válidos.
6. Conclusão
Neste tutorial, vimos como podemos utilizar as funcionalidades deJUnitParams em poucas palavras.
Também abordamos diferentes abordagens que a biblioteca nos fornece para fornecer parâmetros de teste para nossos métodos de teste - muito além do que a própria JUnit pode fazer.
Como sempre, o código-fonte pode ser encontradoover on GitHub.