Ir para o conteúdo principal
Background Image

Facilitando a gestão de dados em testes Java com Instancio

·6 minutos·
Rafael Issao
Autor
Rafael Issao
Apaixonado por tecnologia, programação e inovação. Adoro compartilhar conhecimento e aprender coisas novas todos os dias.
Tabela de conteúdos

Um dos atributos de qualidade de um teste automatizado é o tamanho da amostra exercitada nos testes.

Um exemplo de um teste simples, mas com amostragem baixa, pode ser mostrado com o seguinte código:

    @Test
    void verifySumOfTwoIntegersIsAlwaysPositive() {
        int umPositivo = 1;
        int outroPositivo = 2;

        assertThat(umPositivo).isPositive();
        assertThat(outroPositivo).isPositive();
        assertThat(Integer.sum(umPositivo, outroPositivo))
                  .isPositive();
    }

Um teste simples que verifica a assertividade da soma pode ser um teste incompleto e falho.

Se usarmos outros dados no teste, por exemplo:

    @Test
    void failingTest() {
        int umPositivo = 1;
        int outroPositivo = 1 + Integer.MAX_VALUE;

        assertThat(umPositivo + Integer.MAX_VALUE).isPositive();
        assertThat(outroPositivo).isPositive();
        assertThat(Integer.sum(umPositivo, outroPositivo))
                .isPositive();
    }

O teste falha, pois ocorre um overflow numérico na soma, que se torna negativa.

Um exemplo simples como este mostra a importância de termos dados e amostras suficientes exercitados nos testes para nos sentirmos mais confiantes sobre o código que estamos testando.

Mas este trabalho nem sempre é simples e exige esforço.

E é aí que entra o Instancio! Ele é uma ferramenta que pode simplificar e facilitar este trabalho.

Irei apresentar dois pontos que, na minha opinião, são interessantes para adotar em um projeto Java/Spring!

🛠️ Facilidade de criar a massa de dados
#

Imagine que temos uma entidade com JPA/Hibernate abaixo:

package br.com.youready.article.d_2026_01_04;


import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.hibernate.validator.constraints.br.CPF;


@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class PessoaFisica {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @NotBlank
    private String name;

    @Email
    private String email;

    @CPF
    private String cpf;

    public static PessoaFisica comEmail(String email) {
        PessoaFisica pessoaFisica = new PessoaFisica();
        pessoaFisica.email = email;

        return pessoaFisica;
    }
}

Criar o objeto manualmente e definir cada propriedade com valor apropriado exige esforço. O Instancio facilita este trabalho gerando valores aleatórios para cada campo automaticamente.

Abaixo mostro algumas das principais funcionalidades que ele oferece.

Criação de objetos simples
#

A forma mais básica de uso. Com uma única linha, você tem um objeto totalmente populado com dados aleatórios, sem campos nulos.

    @Test
    void simpleObjectCreationExample() {
        // Por padrão, todos os campos são populados com dados aleatórios!
        PessoaFisica pessoaFisicaParaTeste = Instancio.create(PessoaFisica.class);

        assertThat(pessoaFisicaParaTeste).hasNoNullFieldsOrProperties();

        // Exemplo de saída: pessoaFisicaParaTeste = PessoaFisica(id=7317, name=GDFVO, email=QJV, cpf=ACMOSMKKYB)
        System.out.println("pessoaFisicaParaTeste = " + pessoaFisicaParaTeste);
    }

    @Test

Criando Listas
#

Precisa de uma lista para testar um fluxo de coleção? O Instancio cria listas com tamanho definido ou aleatório num piscar de olhos.

        // Se não especificar o tamanho, ele cria uma lista de tamanho aleatório entre 2 e 6 elementos
        List<PessoaFisica> listaDePF = Instancio.ofList(PessoaFisica.class)
                                                .size(3)
                                                .create();

        assertThat(listaDePF).hasSize(3)
                             .allSatisfy(pessoaFisica -> assertThat(pessoaFisica).hasNoNullFieldsOrProperties());

        // Exemplo de saída: [PessoaFisica(id=3373, name=LEUQQSPPR, email=ZJHSDAVXVI, cpf=SYHZ),
        //      PessoaFisica(id=3724, name=ZZMIOSBKN, email=SXFXOF, cpf=CWEKYSUFQX),
        //      PessoaFisica(id=8790, name=MRMMR, email=PAAAD, cpf=RGOTNDXWF)]
        System.out.println("listaDePF = " + listaDePF);
    }

    @Test
    void populateOtherMandatoryFields() {
        String emailParaTeste = "teste@teste.com.br";
        // É possível popular o objeto após a definição de alguns dados
        PessoaFisica pessoaFisica = PessoaFisica.comEmail(emailParaTeste);

Populando objetos existentes
#

Se você já tem um objeto parcialmente criado (com dados específicos para o teste) e quer preencher o restante, use o fill.


        assertThat(pessoaFisica)
                .hasNoNullFieldsOrProperties()
                .extracting(PessoaFisica::getEmail)
                .isEqualTo(emailParaTeste);

        // Exemplo de saída: PessoaFisica(id=6265, name=BMDJDFGYH, email=teste@teste.com.br, cpf=EZPQVB)
        System.out.println("pessoaFisica = " + pessoaFisica);
    }

    @Test
    void useJakartaOrHibernateContraints() {
        // Instancio suporta:
        // jakarta.validation.constraints
        // javax.validation.constraints
        // org.hibernate.validator.constraints
        PessoaFisica pessoaFisica = Instancio.of(PessoaFisica.class)

Suporte a Bean Validation
#

O Instancio entende anotações do Jakarta Validation (@NotNull, @Email, @CPF, etc.) e gera dados válidos que respeitam essas restrições. Basta habilitar nas configurações.

                                             .withSetting(Keys.JPA_ENABLED, true)
                                             .create();

        assertThat(pessoaFisica).hasNoNullFieldsOrProperties();

        // Exemplo de saída: PessoaFisica(id=5966, name=MFARYO, email=7hw1@wy.org, cpf=41474030777)
        // Observe que foi gerado um email e cpf válido aleatório.
        System.out.println("pessoaFisica = " + pessoaFisica);
    }

    @Test
    void useGenerator() {
        // É possível gerar os valores dos campos de acordo com a sua necessidade
        PessoaFisica pessoaFisica = Instancio.of(PessoaFisica.class)
                                             .withSetting(Keys.BEAN_VALIDATION_ENABLED, true)
                                             .withSetting(Keys.JPA_ENABLED, true)
                                             .generate(Select.field(PessoaFisica::getName), generator -> generator.oneOf("Rafael", "Issao", "Thais"))
                                             .create();

Personalizando dados
#

Quando você precisa de valores específicos (como escolher um item de uma lista pré-definida), o método generate te dá controle total.

        assertThat(pessoaFisica).hasNoNullFieldsOrProperties()
                                .extracting(PessoaFisica::getName)
                                .isIn("Rafael", "Issao", "Thais");

        // Exemplo de saída: PessoaFisica(id=4142, name=Rafael, email=7@15z.com, cpf=38372761639)
        // O 'name' vai ser um dos três valores especificados
        System.out.println("pessoaFisica = " + pessoaFisica);
    }

    @Test
    void defineAModelAndUseInAnyTest() {
        // Defina o modelo e aproveite para utilizar em qualquer classe de teste que precise do objeto
        Model<PessoaFisica> modeloParaPF = Instancio.of(PessoaFisica.class)
                                             .withSetting(Keys.BEAN_VALIDATION_ENABLED, true)
                                             .withSetting(Keys.JPA_ENABLED, true)
                                             .generate(Select.field(PessoaFisica::getName), generator -> generator.oneOf("Rafael", "Issao", "Thais"))
                                             .toModel();

        PessoaFisica pessoaFisica = Instancio.create(modeloParaPF);

        assertThat(pessoaFisica).hasNoNullFieldsOrProperties()
                                .extracting(PessoaFisica::getName)
                                .isIn("Rafael", "Issao", "Thais");

Reutilizando modelos
#

Para evitar duplicação de configuração de testes, você pode definir um Model e reutilizá-lo em qualquer lugar.

    }
}

E tem muito mais, confira na documentação do Instancio!

🧪 Integrado com JUnit Jupiter
#

É possível utilizar o Instancio de forma integrada com JUnit Jupiter. Ele traz algumas facilidades integradas que aceleram ainda mais a escrita de testes.

Para isso, é necessário usar a extensão @ExtendWith(InstancioExtension.class).

Injeção automática (@Given)
#

Esqueça a chamada manual do Instancio.create. Com @Given e @WithSettings, o objeto já chega pronto no seu teste.

import org.instancio.junit.*;
import org.instancio.settings.Keys;
import org.instancio.settings.Settings;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;

import static org.assertj.core.api.Assertions.assertThat;

// Anotação obrigatória para usar, por exemplo, @Given, @WithSettings, @Seed e @InstancioSource
@ExtendWith(InstancioExtension.class)
public class InstancioIntegratedWithJunitJupiterExampleTest {

    @Given
    PessoaFisica pessoaFisica;

    // Possibilidade de configurar o Instancio na classe
    @WithSettings
    private static final Settings settings = Settings.create()
                                              .set(Keys.BEAN_VALIDATION_ENABLED, true)
                                              .set(Keys.JPA_ENABLED, true);

    @Test
    void noNeedToUseInstancioDirectly() {
        // Injetado via @Given
        assertThat(pessoaFisica).hasNoNullFieldsOrProperties();

        System.out.println("pessoaFisica = " + pessoaFisica);
    }

Data Driven Testing (@InstancioSource)
#

O destaque vai para o @InstancioSource. Com ele conseguimos aumentar drasticamente o tamanho da amostragem de um teste. Por padrão, ele executa o teste com 100 dados aleatórios diferentes, garantindo que seu código funcione para uma ampla gama de entradas.

    @ParameterizedTest
    @InstancioSource
    void letsUseTheRandomDataMultipleTimes(PessoaFisica pessoaFisica) {
        // Data Driven Testing. 100 amostras é o valor padrão
        assertThat(pessoaFisica).hasNoNullFieldsOrProperties();

        System.out.println("pessoaFisica = " + pessoaFisica);
    }

Reproduzindo falhas (@Seed)
#

“Mas se o dado é aleatório, como eu reproduzo um erro?”

O Instancio pensou nisso. Quando um teste falha, ele imprime no console a seed (semente) usada naquela execução. Basta adicionar a anotação @Seed com esse valor e o teste rodará exatamente com os mesmos dados, permitindo o debug.

    @Test
    @Seed(666919272848427237L) // Esta anotação garante que será gerado os mesmos dados para facilitar o debug
    void aSeedIsProvidedWhenErrorOccurs() {
        // Esse teste falha com dados aleatórios, mas o instancio provê a semente utilizada para gerar os dados aleatórios:
        // O texto abaixo é fornecido quando um teste falha:
        //  Test method 'aSeedIsProvidedWhenErrorOccurs' failed with seed: 666919272848427237 (seed source: random seed)
        assertThat(pessoaFisica).hasNoNullFieldsOrProperties()
                .extracting(PessoaFisica::getName)
                .isEqualTo("Nome não existente");

        System.out.println("pessoaFisica = " + pessoaFisica);
    }

📚 Referências
#

Para saber mais, recomendo a leitura dos guias oficiais:

✨ Conclusão
#

Muitas vezes, a preguiça de criar massa de dados faz com que escrevamos testes viciados e com baixa cobertura de cenários.

O Instancio remove essa barreira, permitindo que foquemos no que realmente importa: o comportamento do nosso código.

Com ele, conseguimos simular cenários complexos, edge cases e garantir que nossa aplicação seja resiliente, tudo isso com poucas linhas de código e integrado ao nosso ciclo de testes com JUnit.

Se você ainda cria objetos manualmente para seus testes, dê uma chance para o Instancio. Sua bateria de testes agradecerá!

E você, já usa alguma ferramenta para gerar massa de dados? Tem alguma dica para compartilhar?

Deixe seu comentário!

🥒🥒 Até o próximo artigo! 🥒🥒