InícioDesafios › Desafio 03

Desafio 03 Nível: Intermediário

O teste que realmente testa, não só cobre

Alcançar 90% de cobertura é fácil: basta chamar o método dentro de um teste e o Salesforce conta as linhas como cobertas, mesmo que você não verifique nada. O problema aparece meses depois, quando alguém muda a regra de negócio por engano, todos os testes continuam verdes e o bug sobe para produção com o selo de aprovado. Este desafio é sobre a diferença entre um teste que executa o código e um teste que prova que o código está certo.

Testes Apex @TestSetup Teste de fronteira Bulk em teste Asserts Isolamento de dados

O cenário

Você recebeu uma classe pronta que já roda em produção. Ela classifica contas pelo faturamento anual e grava o resultado no campo Rating. A regra é simples e o código funciona. O que falta é o teste, e é ele o seu entregável.

public with sharing class ClassificadorReceita {

    // Classifica cada conta pelo faturamento anual (AnnualRevenue):
    //   maior ou igual a 1.000.000  vira 'Hot'
    //   maior ou igual a 100.000    vira 'Warm'
    //   abaixo disso, ou nulo,      vira 'Cold'
    // Grava o Rating das contas recebidas. Lista nula ou vazia nao faz nada.
    public static void classificar(List<Account> contas) {
        if (contas == null || contas.isEmpty()) {
            return;
        }
        for (Account c : contas) {
            Decimal receita = c.AnnualRevenue;
            if (receita != null && receita >= 1000000) {
                c.Rating = 'Hot';
            } else if (receita != null && receita >= 100000) {
                c.Rating = 'Warm';
            } else {
                c.Rating = 'Cold';
            }
        }
        update contas;
    }
}

Repare nas três faixas e nos dois valores de fronteira: exatamente 1.000.000 e exatamente 100.000. É nas fronteiras que os bugs se escondem, e é ali que um teste decorativo passa sem perceber que a regra foi trocada.

O que você deve construir

Crie a classe de teste ClassificadorReceitaTest, com este contrato mínimo:

@isTest
private class ClassificadorReceitaTest {
    // seus metodos de teste aqui
}

Ela precisa provar que ClassificadorReceita.classificar faz exatamente o que a regra descreve, e continuar cobrindo a classe inteira.

Regras de aceite

São elas que separam o teste que testa do teste que só cobre:

  • Os dados nascem no próprio teste, via @TestSetup. Nada de @isTest(SeeAllData=true): o teste não pode depender de nenhuma conta que já exista na org, porque na org de outra pessoa esse dado não estará lá.
  • Existe um assert para cada faixa, incluindo os valores exatos de fronteira (1000000 e 100000). O teste tem que ficar vermelho se alguém trocar >= por > ou trocar Hot por Warm.
  • Um método de teste classifica 200 contas numa única chamada e verifica que todas saíram com o Rating esperado, sem estourar limite de governança.
  • Caminho negativo: chamar o método com lista nula e com lista vazia não lança exceção e não dispara DML.
  • Conta com AnnualRevenue em branco termina como Cold, nunca sem Rating nem com erro.
  • Todo assert compara o valor esperado e carrega uma mensagem. Contar quantas contas foram atualizadas não prova nada sobre a regra.

O que este desafio avalia

  • Se você entende que cobertura e teste são coisas diferentes: cobrir linha não é verificar resultado.
  • Se você isola o teste do estado da org, criando o próprio dado em vez de confiar no que já existe.
  • Se você testa a fronteira da regra, que é onde mora a maioria dos bugs de classificação.
  • Se você exercita o método em massa, e não com um registro só.
Travei. Me dá uma direção (sem entregar a resposta)

Quatro perguntas que destravam o desafio:

  • Como criar dados que existem só durante o teste, sem tocar na org real e sem repetir o insert em cada método? Pesquise por "Apex @TestSetup method" e lembre que SeeAllData é false por padrão, que é justamente o que você quer.
  • O método faz update e não devolve nada. Como conferir o resultado? Depois de chamar, você consulta as contas de novo dentro do teste. Pesquise por "SOQL inside Apex test".
  • Como escrever um assert que quebra quando a regra muda? Em vez de checar "o Rating não está vazio", cheque "o Rating é exatamente Warm para faturamento de 100 mil". Pesquise por "System.assertEquals message Apex".
  • Qual Rating uma conta com exatamente 1.000.000 deve ter? E uma com 999.999,99? Testar os dois lados de cada fronteira é o que separa o teste de verdade do teste decorativo. Procure "boundary value testing".

Para o bloco de 200 contas, procure "Test.startTest stopTest governor limits": isolar a chamada em massa dentro desse par prova que ela cabe sozinha nos limites de uma transação.

Como testar

A classe de teste você roda pelo Developer Console (Test > New Run) ou pela CLI (sf apex run test). Verde na primeira execução é o mínimo. Mas o critério de verdade deste desafio não é o teste passar: é ele falhar quando o código está errado. Prove isso sabotando o método de propósito:

// Prova objetiva: sabote ClassificadorReceita e rode o teste de novo.
// Troque a linha
//     } else if (receita != null && receita >= 100000) {
// por
//     } else if (receita != null && receita > 100000) {
//
// Agora uma conta com faturamento de exatamente 100.000 cai em 'Cold'
// em vez de 'Warm'. Rode o teste de novo:
//   - se ele ficar VERMELHO, ele esta testando a regra de verdade;
//   - se continuar VERDE, ele so cobre linha e nao prova nada.
// Desfaca a sabotagem depois de confirmar.

Esse é o experimento que revela a qualidade do teste. Cobertura mede quanto do código rodou; a sabotagem mede quanto do comportamento você realmente prendeu.

Gabarito comentado

A classe de teste completa, destrinchada parte por parte: por que @TestSetup, como testar as fronteiras, o bloco de 200 contas, o caminho negativo e como saber se o seu teste realmente testa. Tente resolver antes de abrir.

Ver o gabarito

Resolveu? O ganho aqui não é a classe de teste: é sair com o hábito de perguntar de todo teste "se eu quebrar a regra, este assert fica vermelho?". Teste que não fica vermelho quando o código erra é falsa segurança, e falsa segurança é pior que teste nenhum.