DEV Community

Cover image for PARE DE TESTAR SEU CÓDIGO DE FORMA BURRA
Daniel Camucatto
Daniel Camucatto

Posted on Edited on Originally published at danielcamucatto.dev.br

PARE DE TESTAR SEU CÓDIGO DE FORMA BURRA

Por Que o Seu Teste Unitário Quebra o Encapsulamento

Se você já se viu usando Reflection (ou similares) no seu código de teste para forçar o acesso a um método private, pare. Você está criando testes frágeis, caros de manter e que, ironicamente, minam a qualidade do seu software.

Este artigo é um alerta sobre uma das práticas de teste mais comuns e prejudiciais: o foco na implementação (o como) em vez do comportamento (o o quê).

1. Introdução: O Teste Falso e o Preço da Fragilidade

No desenvolvimento moderno, a busca por alta cobertura de código (code coverage) é constante. No entanto, muitos desenvolvedores confundem a métrica com qualidade. Eles se preocupam em cobrir todas as linhas, inclusive as internas, criando o que chamamos de testes frágeis.

Um teste frágil é aquele que quebra quando você refatora o código, mesmo que o comportamento externo e a funcionalidade final da classe permaneçam idênticos. Ele se torna um fardo, não uma garantia. A raiz desse problema? Ignorar o Encapsulamento.

2. Pilar Fundamental: O Que é Encapsulamento (E por que ele é sagrado)

O encapsulamento é um dos pilares da Programação Orientada a Objetos (POO) e é crucial para a manutenibilidade do código.

Pense na sua Máquina de Café:

  1. Interface Pública (public): São os botões que você usa: ligar(), selecionarExpresso(), adicionarÁgua(). Você interage com a máquina SÓ através destes pontos.

  2. Detalhes Privados (private): São os processos internos: aquecerResistencia(), moerGraos(), bombearÁgua(). Estes métodos são auxiliares, internos à classe, e seu acesso é proibido para o mundo exterior.

A Quebra da Cápsula

O teste unitário deve validar que a máquina de café produz um "Expresso" (o resultado esperado) quando você aperta o botão selecionarExpresso() (o método público).

Quando você usa Reflection para forçar o teste do método moerGraos() (o método privado), você está abrindo a máquina, quebrando sua proteção e expondo a complexidade desnecessariamente.

3. A Crítica Principal: Por Que Testar Métodos Privados é Burro

Testar métodos privados diretamente é um anti-padrão de teste. Chamamos de "burro" porque ele é caro e contraproducente:

Custo 1: Alto Custo de Manutenção

Se amanhã você decide que moerGraos() precisa ser renomeado para processarInsumos() ou ser dividido em dois métodos (moer() e peneirar()), o seu código de produção não quebra. Nenhuma outra classe se importa com isso.

No entanto, o seu teste unitário frágil quebra, porque ele estava acoplado ao nome exato do método privado. Você gastou tempo escrevendo um teste que lhe pune por melhorar o seu código.

Custo 2: Violação do Contrato Público

O objetivo do teste é garantir que o Contrato Público da sua classe seja cumprido.

Se você está testando uma classe ValidadorDeCPF, o que importa é que o método public validar(cpf) retorne true ou false. Não importa se ele usa um método privado chamado calcularDigitos(), verificarRegra1() ou verificarRegra2().

Se o método público funciona, ele já está provando que todos os métodos privados que o suportam estão funcionando.

Custo 3: Confusão de Papéis

Se o seu método privado é tão complexo ou crítico que você sente a necessidade de testá-lo isoladamente, isso é um Code Smell (cheiro de código ruim).

A Solução não é testar o método privado. A Solução é movê-lo para a sua própria classe.

Quando você move a lógica complexa de um método privado para uma nova classe pública (por exemplo, ExtratorDeNivel), você pode testá-la de forma limpa, sem Reflection e sem quebrar o encapsulamento.

4. A Solução Inteligente: Testando o Comportamento, Não Detalhes

Vamos voltar ao caso que motivou esta discussão, onde um método privado (extractNumeroNivel) estava sendo testado diretamente, como visto na thread inicial.

O Teste Burro (Com Reflection em PHP)

// Teste Frágil, com acoplamento interno (EVITAR ESTE TESTE)
// Este é o cenário exato que o David criticou!
public function testExtractNumeroNivelParsesCorrectly(): void
{
    // 1. Usa ReflectionClass para obter a classe
    $ref = new ReflectionClass(NivelDynamic::class); 
    // 2. Obtém o método privado 'extractNumeroNivel'
    $method = $ref->getMethod('extractNumeroNivel'); 
    // 3. Torna o método acessível (QUEBRA O ENCAPSULAMENTO)
    $method->setAccessible(true); 

    // 4. Invoca o método diretamente
    $this->assertEquals('P', $method->invokeArgs($this->nivelDynamic, ['3 - Silver']));
}
Enter fullscreen mode Exit fullscreen mode

Problema: Se o nome do método privado (extractNumeroNivel) for alterado, o teste quebra.

O Teste Inteligente (No Comportamento em PHP)

Assumindo que a classe NivelDynamic tem um método público chamado processarOferta($dados):

// Teste Robusto, focado no comportamento (APROVE ESTE TESTE)
public function testProcessarOfertaComNivelSilver(): void
{
    $dadosEntrada = ['descricao' => '3 - Silver'];
    $oferta = $this->nivelDynamic->processarOferta($dadosEntrada);

    // Se o método público entrega o resultado esperado ('P'), 
    // isso prova que o método privado 'extractNumeroNivel' funcionou internamente.
    $this->assertEquals('P', $oferta['NIVEL_FINAL']);
}
Enter fullscreen mode Exit fullscreen mode

Vantagem: O teste inteligente não se importa como o nível foi extraído, apenas que o resultado final da oferta está correto. Você pode refatorar a lógica interna do private extractNumeroNivel à vontade; enquanto o processarOferta funcionar, o teste passa. O teste agora é uma garantia de funcionalidade, não um fiscal de implementação.

5. Conclusão: Melhores Práticas e Dicas de Teste

O verdadeiro teste unitário é aquele que atua como uma especificação do seu contrato público.

Em resumo, adote estas melhores práticas:

  • Se for private: Ele é um detalhe de implementação. Teste-o através do método public que o chama.

  • Se for complexo demais para ser testado indiretamente: Ele não deve ser private. Ele deve ser extraído para sua própria classe e se tornar público em seu próprio contexto.

Passe a criar testes que garantem o valor que seu código entrega, e não testes que punem você por torná-lo mais limpo e elegante. Pare de testar de forma burra.

Top comments (0)