Ir para o conteúdo principal
Background Image

Como criar um teste unitário simples, rápido e eficiente para um método longo?

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

Você quer começar a escrever testes. Mas o projeto que você trabalha tem muito código legado (ou seja sem teste) e por consequência existem muitos métodos longos.

Para piorar, você precisa alterar um método com, pelo menos, 100 linhas de código. E pior, dentro do método tem chamadas para outros métodos.

Você e sua equipe entendem que criar um teste unitário para este caso não vale a pena pois o custo para compreensão e criação do ambiente de testes (mocks e dados) são custosos.

E a solução sempre gira em torno de um teste mais custoso como teste de integração ou teste E2E. Ou simplesmente não cria testes.

E seu eu te disser que existe uma maneira simples e eficiente para testar somente a sua modificação?

Neste artigo, vamos explorar esta técnica passo a passo.

Exemplo de código
#

package br.com.youready.article.d_2025_01_21.image1;

import java.util.ArrayList;
import java.util.List;

public class OrderProcessor {
    public void processOrders(List<Order> orders) {
        double totalRevenue = 0;
        List<Order> failedOrders = new ArrayList<>();

        System.out.println("Validating orders... ");
        for (Order order : orders) {
            if (order == null || order.items() == null || order.items().isEmpty()) {
                assert order != null;
                System.out.println("Invalid order: " + order.id());
                failedOrders.add(order);
            }
        }

        System.out.println("Processing valid orders... ");
        for (Order order : orders) {
            if (failedOrders.contains(order)) {
                continue;
            }

            try {
                double totalOrder = 0;
                for (Item item : order.items()) {
                    totalOrder += item.price() * item.quantity();
                }

                totalRevenue += totalOrder;

                System.out.println("Processed order: " + order.id() + " with total of " + totalOrder);

            } catch (Exception e) {
                System.err.println("Failed to process order: " + order.id());
                failedOrders.add(order);
            }
        }

        System.out.println("Order processing complete. Total revenue: " + totalRevenue);
    }
}

O código acima valida as ordens e calcula o total de lucro que tivemos.

Mas um novo requisito chega para a equipe.

Como estamos chegando no black friday, você precisa aplicar um desconto de 25% para teclado e mouse.

Para isso, precisamos modificar a seguinte parte do código:

                for (Item item : order.items()) {
                    totalOrder += item.price() * item.quantity();
                }

                totalRevenue += totalOrder;

Como testamos somente a alteração da parte comentada e ignoramos o resto do código já existente?

Ou seja, como criamos um teste unitário simples que não passe pelo método inteiro?

Primeiro passo: Extrair o trecho do código que será alterado
#

Vamos extrair em um método o trecho do código comentado:

package br.com.youready.article.d_2025_01_21.image2;

import java.util.ArrayList;
import java.util.List;

public class OrderProcessor {

    public void processOrders(List<Order> orders) {
        double totalRevenue = 0;
        List<Order> failedOrders = new ArrayList<>();

        System.out.println("Validating orders...");
        for (Order order : orders) {
            if (order == null || order.items() == null || order.items().isEmpty()) {
                assert order != null;
                System.out.println("Invalid order: " + order.id());
                failedOrders.add(order);
            }
        }

        System.out.println("Processing valid orders...");
        for (Order order : orders) {
            if (failedOrders.contains(order)) {
                continue;
            }

            try {
                double totalOrder = getTotalOrder(order);
                totalRevenue += totalOrder;
                System.out.println("Processed order: " + order.id() + " with total of " + totalOrder);
            } catch (Exception e) {

                System.err.println("Failed to process order: " + order.id());
                failedOrders.add(order);
            }
        }

        System.out.println("Order processing complete. Total revenue: " + totalRevenue);
    }

    private static double getTotalOrder(Order order) {
        double totalOrder = 0;
        for (Item item : order.items()) {
            totalOrder += item.price() * item.quantity();
        }
        return totalOrder;
    }
}

Ou seja, o que precisamos testar fica mais claro, simples e conhecemos a dependência para testar este método:

  • Order order

Legal!

Poderíamos tornar o método público, começar a testar e fazer a alteração.

É um caminho válido. Alguns podem argumentar que tornar um método privado para público para testes não é uma boa ideia, mas pelo bem da qualidade, eu prefiro torná-lo público.

Mas nesta situação específica podemos fazer melhor.

Segundo passo: Analisar o novo método e descobrir o melhor lugar para ele
#

Se olharmos o método novo, percebemos que ele só depende do Order e as informações contidas nele. Estamos pedindo informações para o Order para calcular o total da ordem.

Quando temos um código que pede informações para o objeto, estamos quebrando o princípio Tell, Don’t Ask! Devemos dizer para o objeto do tipo Order calcular o total e não pedir informações para ele!

Uma nova refatoração poderia ser assim:

package br.com.youready.article.d_2025_01_21.image3;

import java.util.List;

record Order(Long id, List<Item> items) {
    public double getTotal() {
        double totalOrder = 0;
        for (Item item : this.items()) {
            totalOrder += item.price() * item.quantity();
        }
        return totalOrder;
    }
}

Agora, além do trecho que vamos alterar ser público, ele está em um contexto muito mais focado.

Conseguimos sair do método longo e agora precisamos somente testar o Order#getTotal()!

E percebam que esse tipo de refatoração mais simples não precisa de testes, pois estamos somente movendo o código para um lugar novo.

Terceiro Passo: Criar o teste
#

Agora sim vamos alterar o comportamento do sistema. Você pode aplicar TDD ou pode escrever o teste depois, mas a ideia principal é que está fácil de escrever o teste:

package br.com.youready.article.d_2025_01_21.image4;

import spock.lang.Specification
import static org.junit.jupiter.api.Assertions.assertEquals

class OrderSpec extends Specification {

    def "Teclado e mouse deve ter um desconto de 25% no blackfriday"() {
        setup:
        Item teclado = new Item('Teclado', 100, 10)
        Item mouse = new Item('Mouse', 50, 5)

        List<String> itensComDesconto = [teclado.name(), mouse.name()]

        Order order = new Order(1L, [teclado, mouse])

        when:
        def total = order.getTotal(itensComDesconto)

        then:
        def expectedTotal = teclado.price() * teclado.quantity() * 0.75
        expectedTotal += mouse.price() * mouse.quantity() * 0.75

        assertEquals(expectedTotal, total)
    }
}

E podemos fazer a modificação necessária no código.

No exemplo acima só escrevi um teste mas o ideal é escrever mais casos de teste para o método novo. Ver se o comportamento funciona para quando a lista está vazia ou null, verificar se precisa normalizar antes de verificar se o item está na lista e etc.

Conclusão
#

Neste artigo exploramos como criar um teste e verificar somente o trecho do código que precisamos alterar.

A ideia é bem simples: Só precisamos extrair ele para um novo método público, verificar as dependências, analisar se existe um lugar melhor e finalmente testar.

Se todos da equipe executarem estes passos todos os dias aos poucos, pode ter certeza que a manutenção, leitura e alteração do código vai ficando mais fácil e menos custoso.

Só precisamos ter disciplina.

Se tiver mais curiosidade sobre o assunto, pode falar comigo!

🥒🥒 Espero que tenham curtido e até o próximo artigo! 🥒🥒