Aumentando a cobertura e reduzindo código de testes com o DataProvider

Matheus Bereta
Sicredi Tech
Published in
3 min readMar 19, 2024

Escrever testes automatizados é crucial para garantir a qualidade de software. No entanto, criar testes para cada cenário possível pode ser trabalhoso e demorado. É aí que o DataProvider do TestNG entra em cena!

O DataProvider é um recurso poderoso que permite fornecer dados de teste para seus testes automatizados de forma dinâmica. Isso significa que você pode escrever um único teste e executá-lo com diferentes conjuntos de dados, aumentando significativamente a cobertura de testes sem a necessidade de escrever mais código.

Na prática

Para exemplificar o uso do DataProvider, vamos utilizar um método que valida a idade de uma pessoa, que para fins didáticos, precisa ter entre 18 e 100 anos.

public boolean isValidAge(int age) {
return age >= 18 && age <= 100;
}

Para validar esse método, podemos utilizar algumas técnicas de testes, como partição de equivalência e análise de valor limite, para termos uma boa cobertura do código. Com isso, teríamos que construir os seguintes testes:

import com.matheusbereta.entities.User;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class UserTest {

private User user;

@BeforeClass
void setUp() {
user = new User();
}

@Test
void shouldReturnFalseWhenAgeIs17() {
Assert.assertFalse(user.isValidAge(17));
}

@Test
void shouldReturnTrueWhenAgeIs18() {
Assert.assertTrue(user.isValidAge(18));
}

@Test
void shouldReturnTrueWhenAgeIs19() {
Assert.assertTrue(user.isValidAge(19));
}

@Test
void shouldReturnTrueWhenAgeIs50() {
Assert.assertTrue(user.isValidAge(50));
}

@Test
void shouldReturnTrueWhenAgeIs99() {
Assert.assertTrue(user.isValidAge(99));
}

@Test
void shouldReturnTrueWhenAgeIs100() {
Assert.assertTrue(user.isValidAge(100));
}

@Test
void shouldReturnFalseWhenAgeIs101() {
Assert.assertFalse(user.isValidAge(101));
}

}

Perfeito, temos uma boa cobertura de testes! Mas e se pudéssemos reduzir a quantidade de testes, dado que as asserções são somente assertTrue ou assertFalse…? É aí que entra o DataProvider! Vamos fazer um refactor nesses testes utilizando esse poderoso recurso do TestNG.

import com.matheusbereta.entities.User;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class UserTest {

@DataProvider(name = "validAgeData")
public Object[][] validAgeData() {
return new Object[][] {
{18},
{19},
{50},
{99},
{100},
};
}

@DataProvider(name = "invalidAgeData")
public Object[][] invalidAgeData() {
return new Object[][] {
{17},
{101},
};
}

private User user;

@BeforeClass
void setUp() {
user = new User();
}

@Test(dataProvider = "validAgeData")
void shouldReturnTrueWhenAgeIsValid(int validAge) {
Assert.assertTrue(user.isValidAge(validAge));
}

@Test(dataProvider = "invalidAgeData")
void shouldReturnFalseWhenAgeIsInvalid(int invalidAge) {
Assert.assertFalse(user.isValidAge(invalidAge));
}

}

Dessa forma temos o nosso código mais organizado, dado que utilizando a annotation @DataProvider, centralizamos todos os dados utilizados em nossos testes e fazemos o reaproveitamento dos scripts de testes.

Com essa abordagem, também podemos acelerar o desenvolvimento de nossos testes, pensando primeiramente nos dados do teste e depois na construção do script.

Particularmente, acho a abordagem acima a ideal pois mescla o reaproveitamento de código com a organização dos dados de teste, porém, podemos reduzir ainda mais essa classe de testes, adicionando o resultado esperado como parâmetro do teste:

import com.matheusbereta.entities.User;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class UserTest {

@DataProvider(name = "ageData")
public Object[][] ageData() {
return new Object[][] {
{17, false},
{18, true},
{19, true},
{50, true},
{99, true},
{100, true},
{101, false},
};
}

private User user;

@BeforeClass
void setUp() {
user = new User();
}

@Test(dataProvider = "ageData")
void shouldReturnIfTheAgeIsValid(int age, boolean expectedResult) {
Assert.assertEquals(user.isValidAge(age), expectedResult);
}

}

Agora temos somente 1 data provider, 1 teste e 7 execuções!

Ah, mas eu uso o JUnit, e agora?!

Fique tranquilo! O JUnit possui um recurso com o mesmo objetivo, porém com algumas diferenças na sintaxe e implementação, chamado de ParameterizedTest. Logo farei um artigo sobre!

Conclusão

Eu concluo que você precisa utilizar o DataProvider urgentemente! kkk

Esse recurso é muito poderoso e com certeza acrescentará muito para os seus testes. Inclusive, ele pode ser utilizado em qualquer tipo ou camada de teste: unitário, integrado, UI, etc.

Vou deixar aqui o link de um projeto de testes de UI, utilizando Selenium WebDriver, aonde utilizo esse recurso do DataProvider.

🍻

--

--