Por que os métodos particulares de teste de unidade são considerados uma má prática?

15

Contexto:

Atualmente, estou trabalhando em um pequeno projeto em Python. Geralmente estruturo minhas classes com alguns métodos públicos documentados, mas principalmente com os conceitos de alto nível (o que um usuário da classe deve conhecer e usar) e vários métodos ocultos (começando com sublinhado) que são responsáveis ​​pelo processamento complexo ou de baixo nível.

Eu sei que os testes são essenciais para garantir a confiança no código e garantir que qualquer modificação posterior não interrompa o comportamento anterior.

Problema:

Para criar os métodos públicos de nível superior em uma base confiável, geralmente testo os métodos privados. Acho mais fácil descobrir se uma modificação de código introduziu regressões e onde. Isso significa que esses testes internos podem violar revisões menores e precisarão ser corrigidos / substituídos

Mas também sei que o método privado de teste de unidade é pelo menos um conceito disputado ou mais frequentemente considerado como uma má prática. A razão é: apenas o comportamento público deve ser testado ( ref. )

Questão:

Eu me preocupo em seguir as melhores práticas e gostaria de entender:

  • por que o uso de testes de unidade em métodos privados / ocultos é ruim (qual é o risco)?
  • Quais são as melhores práticas quando os métodos públicos podem usar processamento de baixo nível e / ou complexo?

Precisões:

  • não é um como questionar. Python não tem um conceito verdadeiro de privacidade e métodos ocultos simplesmente não estão listados, mas podem ser usados ​​quando você souber o nome deles
  • Nunca aprendi regras e padrões de programação: minhas últimas aulas são dos anos 80 ... Aprendi principalmente idiomas por tentativa e falha e referências na Internet (o Stack Exchange é o meu favorito há anos)
Serge Ballesta
fonte
2
Possível duplicado de métodos privados de provas como protegida
Greg Burghardt
2
OP, onde você ouviu ou leu que o teste de métodos privados era considerado "ruim"? Existem diferentes maneiras de testar a unidade. Consulte Teste de unidade, teste de caixa preta e teste de caixa branca .
19718 John Wu
@ JohnWu: Eu sei a diferença entre os testes de caixa branca e caixa preta. Mas, mesmo nos testes da caixa branca, parece que a necessidade de testar métodos privados é uma dica para um problema de design. A minha pergunta é uma tentativa de compreender quais são os melhores caminhos quando eu cair lá ...
Serge Ballesta
2
Novamente, onde você ouviu ou leu que, mesmo nos testes de caixa branca, a necessidade de testar métodos privados é uma dica para um problema de design? Eu gostaria de entender o raciocínio por trás dessa crença antes de tentar uma resposta.
19418 John Wu
@SergeBallesta em outras palavras, coloque algumas referências aos artigos que fizeram você acreditar que testar métodos privados é uma prática ruim. Então explique-nos por que você acredita neles.
LAIV

Respostas:

17

Um par de razões:

  1. Normalmente, quando você é tentado a testar o método privado de uma classe, é um cheiro de design (classe de iceberg, componentes públicos reutilizáveis ​​insuficientes etc.). Quase sempre há algum problema "maior" em jogo.

  2. Você pode testá-los através da interface pública (que é como você deseja testá-los, porque é assim que o cliente os chama / usa). Você pode obter uma falsa sensação de segurança vendo a luz verde em todos os testes aprovados nos seus métodos particulares. É muito melhor / mais seguro testar casos extremos em suas funções privadas por meio de sua interface pública.

  3. Você corre o risco de duplicação severa de testes (testes que parecem muito similares) testando métodos privados. Isso tem grandes conseqüências quando os requisitos mudam, pois mais testes do que o necessário serão interrompidos. Ele também pode colocá-lo em uma posição em que é difícil refatorar por causa do seu conjunto de testes ... o que é a ironia final, porque o conjunto de testes existe para ajudá-lo a redesenhar e refatorar com segurança!

Uma dica, se você ainda está tentado a testar as partes privadas (não a use se isso a incomoda e ao YMMV, mas funcionou bem para mim no passado): Às vezes, escrevendo testes de unidade para funções privadas apenas para garantir eles estão trabalhando exatamente como você pensa que podem ser valiosos (especialmente se você é novo em um idioma). No entanto, depois de ter certeza de que eles funcionam, exclua os testes e sempre verifique se os testes voltados ao público são sólidos e serão capturados se alguém fizer uma alteração flagrante na referida função privada.

Quando testar métodos particulares: Como essa resposta se tornou (um pouco) popular, sinto-me obrigado a mencionar que uma "melhor prática" é sempre apenas isso: uma "melhor prática". Isso não significa que você deve fazer isso dogmaticamente ou cegamente. Se você acha que deveria testar seus métodos particulares e ter um motivo legítimo (como se estivesse escrevendo testes de caracterização para um aplicativo herdado), teste seus métodos privados . Circunstâncias específicas sempre superam qualquer regra geral ou melhor prática. Esteja ciente de algumas das coisas que podem dar errado (veja acima).

Eu tenho uma resposta que detalha isso em detalhes no SO, que não repetirei aqui: /programming/105007/should-i-test-private-methods-or-only-public-ones/ 47401015 # 47401015

Matt Messersmith
fonte
4
Razão 1: Nebuloso. Razão 2: e se o seu método auxiliar privado não fizer parte da API pública? Razão 3: não se você projetar sua classe corretamente. Sua última dica: por que eu excluiria um teste perfeitamente bom que comprove que um método que escrevi funciona?
Robert Harvey
4
@RobertHarvey Razão 2: estar indiretamente acessível através da API pública! = Fazer parte da API pública. Se sua função privada não pode ser testada por meio da API pública, talvez seja um código morto e deva ser removido? Ou sua classe é de fato um iceberg (motivo 1) e deve ser refatorada.
Frax
5
@RobertHarvey se você não puder testar uma função privada por meio de uma API pública, exclua-a, pois ela não serve para nada.
David Arno
11
@RobertHarvey 1: Os cheiros de design são sempre algo subjetivos / nebulosos, com certeza. Mas listei alguns exemplos concretos de antipadrões, e há mais detalhes na minha resposta SO. 2: Métodos privados não podem fazer parte da API pública (por definição: eles são privados) ... portanto, não acho que sua pergunta faça muito sentido. Estou tentando chegar ao ponto de que, se você tem algo como bin_search(arr, item)(público) e bin_search(arr, item, low, hi)(privado, há muitas maneiras de fazer pesquisa em bin), tudo o que você precisa para testar é o público ( bin_search(arr, item))
Matt Messersmith
11
@RobertHarvey 3: Em primeiro lugar, eu disse risco , não garantia . Em segundo lugar, alegar que "funciona se você o fizer corretamente" é auto-realizável. Por exemplo, "Você pode escrever um sistema operacional em uma única função, se fizer isso corretamente ". Isso não é falso: mas também não é útil. Sobre a dica: você a excluiria porque, se sua implementação mudar (por exemplo, você deseja trocar um implemento privado), seu conjunto de testes ficará no seu caminho (você terá um teste com falha onde não deveria).
Matt Messersmith
13

Dado que um dos principais objetivos dos testes de unidade é que você pode refatorar as partes internas do seu programa e poder verificar se não quebrou sua funcionalidade, é contraproducente se seus testes de unidade operarem com um nível de granularidade tão bom que qualquer alteração no código do programa exige que você reescreva seus testes.

Pete
fonte
Não sei por que sua resposta foi reduzida. É curto, direto ao ponto e obtém a resposta 100% correta.
David Arno
3
@ DavidArno: Talvez porque testar métodos privados realmente não tenha muito a ver com granularidade de teste. Tem tudo a ver com o acoplamento aos detalhes da implementação.
Robert Harvey
10

Escrever testes de unidade para métodos particulares vincula seus testes de unidade aos detalhes da implementação.

Os testes de unidade devem testar o comportamento de uma classe na superfície externa da classe (é API pública). Os testes de unidade não precisam saber nada sobre as entranhas de uma classe. Escrever testes de unidade nos detalhes de implementação de uma classe amarra suas mãos quando chega a hora de refatorar. A refatoração quase certamente vai quebrar esses testes, porque eles não fazem parte da sua API estável.

Dito isto, por que você pode querer escrever testes de unidade para seus métodos particulares?

Há uma tensão natural entre testes de unidade e desenvolvimento incremental. Os desenvolvedores de software que usam um REPL (loop de leitura e avaliação) podem atestar o quão produtivo pode ser para escrever e testar rapidamente pequenos pedaços de funcionalidade à medida que você "cresce" uma classe ou função. A única boa maneira de fazer isso em ambientes controlados por teste de unidade é escrever testes de unidade para métodos privados, mas há muito atrito em fazer isso. Os testes de unidade levam tempo para serem escritos, você precisa de um método real para testar e sua estrutura de teste precisa oferecer suporte à capacidade de manter o método privado, para que não polua sua API externa.

Alguns ecossistemas como C # e .NET têm maneiras de criar ambientes semelhantes a REPL (ferramentas como o Linqpad fazem isso), mas seu utilitário é limitado porque você não tem acesso ao seu projeto. A janela imediata no Visual Studio é inconveniente; ele ainda não possui o Intellisense completo, é necessário usar nomes totalmente qualificados e acionar uma compilação toda vez que você o usar.

Robert Harvey
fonte
4
@ user949300 É um pouco difícil debater isso sem cair na verdadeira falácia do escocês , mas há muitos testes mal escritos de todos os tipos. De uma perspectiva de teste de unidade, você deve testar o contrato público do seu método sem conhecer os detalhes da implementação interna. Não que afirmar que uma certa dependência tenha sido chamada X vezes esteja sempre errado: há situações em que isso faz sentido. Você só precisa se certificar de que essa é uma informação que você realmente deseja transmitir no contrato dessa unidade em teste.
Vincent Savard
3
@DavidArno: [encolher os ombros] Eu venho fazendo isso há algum tempo agora. Os testes de unidade para métodos privados sempre funcionaram bem para mim, até que a Microsoft decidiu parar de oferecer suporte a objetos proxy em sua estrutura de teste. Nada jamais explodiu como resultado. Eu nunca abri um buraco no universo escrevendo um teste para um método privado.
Robert Harvey
2
@DavidArno: Por que eu pararia de usar uma técnica perfeitamente boa que me proporcione benefícios, apenas porque alguém na internet diz que é uma má ideia sem fornecer qualquer justificativa?
Robert Harvey
2
O principal benefício que recebo dos testes de unidade é fornecer uma "rede de segurança" que me permita mexer no meu código e ter certeza de que minhas alterações não estão introduzindo regressões. Para esse fim, o teste de métodos auxiliares particulares facilita a localização de tais regressões. Quando refato um método auxiliar privado e introduzo um erro lógico, interrompo testes específicos para esse método privado. Se meus testes de unidade fossem mais gerais e testassem apenas a interface dessa unidade de código, o problema seria muito mais obscuro.
Alexander - Restabelece Monica
2
@ Frax Claro, eu poderia, mas por essa lógica, devo renunciar aos testes de unidade em favor dos testes de integração em todo o sistema. Afinal, "na maioria dos casos, você deve poder modificar esses testes para testar o mesmo comportamento"
Alexander - Reinstate Monica
6

Pela minha experiência, descobri que os testes unitários das classes internas, métodos geralmente significa que eu tenho que retirar as funções testadas, as classes. Para criar outro nível de abstração.

Isso leva a uma melhor aderência ao Princípio da Responsabilidade Única.

Robert Andrzejuk
fonte
Essa deve ser a resposta aceita.
Jack Aidley
0

Penso que esta é uma boa pergunta, pois expõe um problema comum na cobertura de testes. Mas uma boa resposta deve lhe dizer que a pergunta não está exatamente correta porque, em teoria, você não deve poder testar métodos particulares . É por isso que eles são privados .

Talvez uma pergunta melhor seja "O que devo fazer quando quiser testar métodos particulares?" , e a resposta é meio óbvia: você deve expô-los de uma maneira que torne possível o teste. Agora, isso não significa necessariamente que você deve apenas tornar público o método e é isso. Muito provavelmente você desejará fazer uma abstração mais alta; vá para uma biblioteca ou API diferente para poder fazer seus testes nessa biblioteca, sem expor essa funcionalidade na sua API principal.

Lembre-se de que há uma razão pela qual seus métodos têm diferentes níveis de acessibilidade e você deve sempre pensar em como suas aulas serão usadas no final.

Juan Carlos Eduardo Romaina AC
fonte
Tentei resolver isso na minha pergunta dizendo que Python não tem verdadeiro conceito de privacidade e métodos ocultos simplesmente não estão listados, mas pode ser usado quando você sabe seu nome
Serge Ballesta