Você prefere tornar coisas privadas internas / públicas para testes ou usar algum tipo de invasão como o PrivateObject?

26

Eu sou um iniciante no teste de código e era uma assertprostituta antes. Uma coisa que me preocupa no teste de unidade é que, muitas vezes, é necessário que você crie public(ou pelo menos internal) campos que teriam sido de privateoutra forma, desuni- readonlylos, faça privatemétodos protected virtual, etc ...

Descobri recentemente que você pode evitar isso usando coisas como a classe PrivateObject para acessar qualquer coisa em um objeto por meio de reflexão. Mas isso torna seus testes menos sustentáveis ​​(as coisas falham na execução, em vez do tempo de compilação, serão quebradas por uma simples renomeação, é mais difícil depurar ...). Qual a sua opinião sobre isso? Quais são as melhores práticas no teste de unidade relacionadas à restrição de acesso?

editar: considere, por exemplo, que você tem uma classe com um cache em um arquivo em disco e, em seus testes, deseja gravar na memória.

Zonko
fonte
2
O pessoal do python só tem público. Eles estão felizes. Por que se preocupar? Por que não tornar tudo público?
S.Lott
12
Porque isso está longe das melhores práticas na maioria dos principais idiomas. A resposta real é usar boas interfaces e tal, para que você possa usar objetos simulados para realizar testes.
Rig
2
@ Rig: Python não é uma linguagem de topo? Interessante.
S.Lott
7
"está longe das melhores práticas na maioria dos principais idiomas" não implica logicamente (ou até alude a isso) "Python não é um idioma de topo".
precisa
1
Precisamente, é um idioma de topo, mas a maioria dos idiomas de topo não é Python e endossa a definição do escopo apropriado. Eu mantenho a minha declaração. Existem padrões projetados para criar softwares altamente testáveis ​​e garantir que as variáveis ​​mantenham o escopo. Até os programadores Python tendem a emular o escopo com prefixos do que vi.
Rig

Respostas:

36

Você nunca deveria ter que

fazer public(ou pelo menos internal) campos que teria sido privatede outra forma, a UN- readonlylos, certifique privatemétodos protected virtualvez

Especialmente não ler um campo pode tornar um objeto imutável mutável, e isso seria um desastre.

Seus testes devem considerar seus objetos como caixas pretas ( Wikipedia ), o que significa que eles devem se preocupar apenas com a interface pública dos objetos, não com detalhes de sua implementação.

Se um objeto não puder ser testado suficientemente usando sua interface pública, você precisará encontrar maneiras de fornecer extensões formais e úteis à sua interface que facilitariam o teste. Por exemplo, um sistema de registro com apenas um par de métodos de registro / cancelamento de registro talvez se beneficie de um método IsRegistered, mesmo que não seja necessário para o aplicativo em questão; ainda assim, é uma extensão formal e útil para a interface e, aliás, acomodará os testes.

O importante é que alterar a implementação do objeto não exija que você altere o teste de unidade. Em teoria, você deve poder escrever o teste de unidade uma vez e depois pedir a vários programadores que codifiquem várias implementações totalmente diferentes do objeto a ser testado para trabalhar com o teste de unidade.

Mike Nakis
fonte
2
Exatamente! Se você não pode testá-lo em unidade sem reflexão ou hacks, provavelmente há um erro na sua API ou no seu design.
Falcon
Você está dizendo que criar privatemétodos protected virtualpara ajudar na zombaria nos testes é considerado uma má prática?
Robula 17/01
1
@Robula No meu livro, sim. Nem escrevo meus testes no mesmo pacote que o código de produção, porque não tenho utilidade para acessar membros não públicos. Também não uso zombaria. Obviamente, se você precisar usar zombarias, precisará fazer o que for necessário para facilitar a zombaria, para que o virtual protegido possa ser um compromisso prático em muitas situações. Mas, idealmente, um projeto não precisa de zombarias, apenas implementações alternativas, e o ideal é testar caixas negras, não caixas brancas, para que as coisas sejam testadas na integração, sem zombarias.
Mike Nakis 17/01
13

Em C #, você pode usar o InternalsVisibleToAttributepara permitir que seu assembly de teste veja as internalclasses no assembly que você está testando. Parece que você já sabe disso.

Na maioria dos casos, estou interessado apenas em testar a API pública do meu assembly. Em um caso em que eu quero fazer testes de caixa branca de alguma unidade, movo o código para uma internalclasse e o faço um publicmétodo dessa classe. Então eu posso codificar um teste de unidade para essa classe.

Isso se presta bem à injeção de dependência e ao padrão de estratégia. Você pode abstrair o método em uma interface, injetar a interface no construtor do objeto pai e apenas testar se o pai delega adequadamente. Em seguida, você pode testar se o objeto filho se comporta adequadamente. Isso torna tudo mais modular.

Scott Whitlock
fonte
8

Na minha experiência, é melhor não expor os internos de sua turma, especialmente para o teste. Mesmo que isso pareça facilitar a gravação do teste. O teste é o primeiro cliente da sua classe; portanto, se for difícil testá-la por meio de sua interface pública, provavelmente será difícil usá-la em outros lugares também.

A facilidade de teste da classe por meio de sua API pública é o feedback sobre a facilidade de uso da classe. Pense nos testes parcialmente como uma maneira de criar um protótipo do uso da sua classe.

Tente considerar por que seu teste precisa detectar algo sobre a classe que não está disponível por meio da API pública. Talvez a sua turma esteja fazendo muito e precise ser decomposta em um grupo de turmas cooperantes? Em seguida, você pode zombar de algumas das classes colaboradoras para sentir o que a classe em teste está fazendo.

Tente aplicar o princípio de inversão de dependência e introduza interfaces entre suas classes, que você pode zombar em seus testes. Você pode fornecer os colaboradores para o seu teste usando inversão de controle .

Considere também aplicar o princípio "diga não pergunte" para ajudar a estruturar sua interface de classe de maneira robusta e pouco acoplada, onde as informações corretas são expostas e o restante é oculto.

flamingpenguin
fonte
6

Pessoalmente, prefiro deixar o código que estou testando o mais puro possível e se o código de teste precisa de hacks (e em ponteiros desagradáveis ​​em C ++ etc.), fico feliz em fazer isso.

Formiga
fonte
1
Eu concordo, é assim que se faz. Mantenha a feiura no código de teste, onde não pode prejudicar nada.
Alan Delimon
@AlanDelimon Não poderia prejudicar a mente dos programadores de manutenção subsequentes?
Flamingpenguin
1
O código feio do @flamingpenguin pode prejudicar os programadores em qualquer lugar; mas o código de teste feio provavelmente causará menos danos, pois afetará apenas as pessoas que modificam a classe e não também as pessoas que simplesmente a consomem.
Dan Neely
2
@flamingpenguin Pode, mas se você tiver que colocar um código feio em algum lugar, ele também poderá estar em um teste de unidade em que é menos provável que precise de modificação e faça parte do aplicativo.
Alan Delimon
5

A visibilidade do seu código não tem nada a ver com seus testes de unidade!

Você torna seus métodos privados, não com o objetivo de ocultá-los dos testes e não os torna públicos para serem expostos aos testes, certo? Esta é a sua implementação que é essencial aqui, não os testes - esses servidores como prova do seu código, mas eles não são o seu código .

Existem várias maneiras de habilitar o acesso a métodos e propriedades ocultos (privados ou internos). Muitas estruturas de teste e IDEs (por exemplo, Visual Studio) oferecem suporte aqui, gerando tudo o que é necessário para o acesso; você só precisa criar seus casos de teste. Então, por que se preocupar? Melhor manter seu código limpo e arrumado.

Alexander Galkin
fonte