DEV Community

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

Posted on

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)