Nota do moderador: já existem 39 respostas postadas aqui (algumas foram excluídas). Antes de postar sua resposta, considere se você pode ou não adicionar algo significativo à discussão. É mais do que provável que você apenas repita o que alguém já disse.
Ocasionalmente, preciso me tornar um método privado em uma classe pública apenas para escrever alguns testes de unidade para ele.
Normalmente, isso ocorre porque o método contém lógica compartilhada entre outros métodos da classe e é mais organizado testar a lógica por conta própria, ou outro motivo pode ser possível: desejo testar a lógica usada em encadeamentos síncronos sem ter que se preocupar com problemas de encadeamento .
Outras pessoas se veem fazendo isso, porque eu realmente não gosto de fazer isso? Pessoalmente, acho que os bônus superam os problemas de tornar público um método que realmente não fornece nenhum serviço fora da classe ...
ATUALIZAR
Obrigado por responder a todos, parece ter despertado o interesse das pessoas. Penso que o consenso geral de que os testes devem ocorrer por meio da API pública, pois é a única maneira de uma classe ser usada, e eu concordo com isso. Os dois casos que eu mencionei acima, onde eu faria isso acima, eram casos incomuns e achei que os benefícios de fazer isso valeram a pena.
No entanto, posso ver que todos apontam que isso nunca deveria realmente acontecer. E, quando penso um pouco mais sobre isso, acho que mudar seu código para acomodar testes é uma péssima idéia - afinal, suponho que o teste seja uma ferramenta de suporte e que mude um sistema para 'suportar uma ferramenta de suporte', se quiser, é flagrante má prática.
fonte
@VisibileForTesting
anotação para fazer esses métodos - eu recomendo que você faça isso para que o motivo do método nãoprivate
seja devidamente documentado.Respostas:
Como uma declaração geral, geralmente sou a favor de refatorar o código de "produção" para facilitar o teste. No entanto, eu não acho que seria uma boa ligação aqui. Um bom teste de unidade (geralmente) não deve se preocupar com os detalhes de implementação da classe, apenas com seu comportamento visível. Em vez de expor as pilhas internas ao teste, você pode testar se a classe retorna as páginas na ordem em que você espera após chamar
first()
oulast()
.Por exemplo, considere este pseudocódigo:
fonte
Pessoalmente, prefiro o teste de unidade usando a API pública e certamente nunca tornaria o método privado público apenas para facilitar o teste.
Se você realmente deseja testar o método privado isoladamente, em Java, você pode usar o Easymock / Powermock para permitir isso.
Você precisa ser pragmático e também deve estar ciente das razões pelas quais as coisas são difíceis de testar.
' Ouça os testes ' - se é difícil testar, isso está lhe dizendo algo sobre seu design? Você poderia refatorar para onde um teste para esse método seria trivial e facilmente coberto por testes através da API pública?
Aqui está o que Michael Feathers tem a dizer em ' Trabalhando efetivamente com o código legado "
fonte
Como outros já disseram, é um pouco suspeito estar testando métodos privados; unit test a interface pública, não os detalhes da implementação privada.
Dito isto, a técnica que eu uso quando quero testar alguma coisa privada em C # é reduzir a proteção de acessibilidade de privada para interna e marcar o assembly de teste de unidade como um amigo usando InternalsVisibleTo . O conjunto de teste da unidade poderá tratar os internos como públicos, mas você não precisa se preocupar em adicionar acidentalmente sua área de superfície pública.
fonte
Muitas respostas sugerem apenas testar a interface pública, mas IMHO isso não é realista - se um método faz algo que leva cinco etapas, você deve testar essas cinco etapas separadamente, nem todas juntas. Isso requer testar todos os cinco métodos, que (exceto os testes) podem ser
private
.A maneira usual de testar métodos "privados" é dar a cada classe sua própria interface e criar os métodos "privados"
public
, mas não os inclui na interface. Dessa forma, eles ainda podem ser testados, mas não incham a interface.Sim, isso resultará em inchaço de arquivo e classe.
Sim, isso torna os especificadores
public
eprivate
redundantes.Sim, isso é um pé no saco.
Infelizmente, este é um dos muitos sacrifícios que fazemos para tornar o código testável . Talvez uma linguagem futura (ou mesmo uma versão futura do C # / Java) tenha recursos para tornar a testabilidade de classe e módulo mais conveniente; mas, enquanto isso, temos que pular esses aros.
Alguns argumentam que cada uma dessas etapas deve ser de sua própria classe , mas eu discordo - se todas compartilham o estado, não há razão para criar cinco classes separadas, onde cinco métodos o fariam. Pior ainda, isso resulta em inchaço de arquivos e classes. Além disso , ele infecta a API pública do seu módulo - todas essas classes devem ser necessariamente
public
se você quiser testá-las em outro módulo (ou incluir o código de teste no mesmo módulo, o que significa enviar o código de teste com o seu produto) .fonte
Um teste de unidade deve testar o contrato público, a única maneira de como uma classe pode ser usada em outras partes do código. Um método privado é o detalhamento da implementação; você não deve testá-lo, pois a API pública funciona corretamente, a implementação não importa e pode ser alterada sem alterações nos casos de teste.
fonte
Na IMO, você deve escrever seus testes sem fazer suposições profundas sobre como sua classe foi implementada por dentro. Você provavelmente deseja refatorá-lo posteriormente usando outro modelo interno, mas ainda dando as mesmas garantias que a implementação anterior fornece.
Tendo isso em mente, sugiro que você se concentre em testar se o seu contrato ainda é válido, independentemente da implementação interna da sua classe. Teste de propriedade de suas APIs públicas.
fonte
Que tal torná-lo privado? Em seguida, seu código de teste pode vê-lo (e outras classes no seu pacote), mas ainda está oculto para seus usuários.
Mas, na verdade, você não deve testar métodos privados. Esses são detalhes de implementação e não fazem parte do contrato. Tudo o que eles fazem deve ser coberto chamando os métodos públicos (se eles tiverem um código que não seja exercido pelos métodos públicos, isso deverá ser feito). Se o código privado é muito complexo, a classe provavelmente está fazendo muitas coisas e quer refatorar.
Tornar público um método é um grande compromisso. Depois que você fizer isso, as pessoas poderão usá-lo e você não poderá mais mudá-las.
fonte
Atualização : eu adicionei uma resposta mais expandida e mais completa a esta pergunta em vários outros lugares. Isso pode ser encontrado no meu blog .
Se eu precisar tornar público algo para testá-lo, isso geralmente indica que o sistema em teste não está seguindo o Princípio Único de Responsabilidade . Portanto, há uma classe ausente que deve ser introduzida. Depois de extrair o código em uma nova classe, torne-o público. Agora você pode testar facilmente e está seguindo o SRP. Sua outra classe simplesmente precisa invocar essa nova classe via composição.
Tornar métodos públicos / usar truques de idioma, como marcar o código como visível para os assemblies de teste, deve sempre ser o último recurso.
Por exemplo:
Refatore isso introduzindo um objeto validador.
Agora tudo o que precisamos fazer é testar se o validador foi chamado corretamente. O processo real de validação (a lógica anteriormente privada) pode ser testado em puro isolamento. Não será necessário configurar testes complexos para garantir que essa validação seja aprovada.
fonte
Algumas ótimas respostas. Uma coisa que não vi mencionada é que, com o desenvolvimento orientado a teste (TDD), métodos privados são criados durante a fase de refatoração (veja Método de extração para obter um exemplo de padrão de refatoração) e, portanto, já deve ter a cobertura de teste necessária. . Se feito corretamente (e, é claro, você terá uma mescla de opiniões quando se trata de correção), não precisa se preocupar em tornar público um método privado apenas para poder testá-lo.
fonte
Por que não dividir o algoritmo de gerenciamento de pilha em uma classe de utilitário? A classe de utilitário pode gerenciar as pilhas e fornecer acessadores públicos. Seus testes de unidade podem ser focados nos detalhes da implementação. Testes profundos para classes complicadas algoritmicamente são muito úteis para eliminar os casos extremos e garantir a cobertura.
Em seguida, sua classe atual pode delegar de maneira limpa para a classe de utilitário, sem expor nenhum detalhe de implementação. Seus testes estarão relacionados ao requisito de paginação, conforme recomendado por outros.
fonte
Em java, há também a opção de torná-lo privado (ou seja, deixando de fora o modificador de visibilidade). Se seus testes de unidade estiverem no mesmo pacote que a classe que está sendo testada, ele poderá ver esses métodos e será um pouco mais seguro do que tornar o método completamente público.
fonte
Métodos particulares são geralmente usados como métodos "auxiliares". Portanto, eles retornam apenas valores básicos e nunca operam em instâncias específicas de objetos.
Você tem algumas opções se quiser testá-las.
Como alternativa, você pode criar uma nova classe com o método auxiliar como um método público, se for um candidato suficientemente bom para uma nova classe.
Há um artigo muito bom aqui sobre isso.
fonte
Se você estiver usando C #, poderá tornar o método interno. Dessa forma, você não polui a API pública.
Em seguida, adicione atributo à dll
[assembly: InternalsVisibleTo ("MyTestAssembly")]
Agora todos os métodos estão visíveis no seu projeto MyTestAssembly. Talvez não seja perfeito, mas é melhor do que tornar público o método privado apenas para testá-lo.
fonte
Use a reflexão para acessar as variáveis privadas, se necessário.
Mas, na verdade, você não se importa com o estado interno da classe, apenas deseja testar se os métodos públicos retornam o que você espera nas situações que você pode antecipar.
fonte
Eu diria que é uma péssima idéia, pois não tenho certeza se você obtém algum benefício ou potencial problema no futuro. Se você está alterando o contrato de uma chamada, apenas para testar um método privado, não está testando a classe em como ela seria usada, mas criando um cenário artificial que você nunca pretendeu que acontecesse.
Além disso, declarando o método como público, o que significa dizer que em seis meses (depois de esquecer que a única razão para tornar um método público é o teste) você (ou se você entregou o projeto) alguém completamente diferente venceu use-o, levando a conseqüências potencialmente não intencionais e / ou a um pesadelo de manutenção.
fonte
em termos de teste de unidade, você definitivamente não deve adicionar mais métodos; Acredito que é melhor você fazer um caso de teste sobre o seu
first()
método, que seria chamado antes de cada teste; então você pode chamar várias vezes o -next()
,previous()
elast()
para ver se os resultados corresponder à sua expectativa. Acho que se você não adicionar mais métodos à sua classe (apenas para fins de teste), você seguirá o princípio da "caixa preta" de teste;fonte
Primeiro, veja se o método deve ser extraído para outra classe e tornado público. Se não for esse o caso, torne-o protegido por pacotes e anote em Java com @VisibleForTesting .
fonte
Na sua atualização, você diz que é bom apenas testar usando a API pública. Na verdade, existem duas escolas aqui.
Teste de caixa preta
A escola da caixa preta diz que a turma deve ser considerada uma caixa preta que ninguém pode ver a implementação dentro dela. A única maneira de testar isso é através da API pública - assim como o usuário da classe o usará.
teste de caixa branca.
A escola de caixa branca pensa que é natural usar o conhecimento sobre a implementação da turma e depois testá-la para saber se ela funciona como deveria.
Eu realmente não posso tomar parte na discussão. Eu apenas pensei que seria interessante saber que existem duas maneiras distintas de testar uma classe (ou uma biblioteca ou qualquer outra coisa).
fonte
Na verdade, existem situações em que você deve fazer isso (por exemplo, ao implementar alguns algoritmos complexos). Basta fazê-lo como pacote privado e isso será suficiente. Mas, na maioria dos casos, provavelmente você tem classes muito complexas, o que exige levar em consideração a lógica para outras classes.
fonte
Métodos particulares que você deseja testar isoladamente são uma indicação de que há outro "conceito" oculto em sua classe. Extraia esse "conceito" para sua própria classe e teste-o como uma "unidade" separada.
Dê uma olhada neste vídeo para uma visão realmente interessante sobre o assunto.
fonte
Você nunca deve deixar seus testes ditarem seu código. Eu não estou falando sobre TDD ou outros DDs, quero dizer, exatamente o que você está perguntando. Seu aplicativo precisa desses métodos para ser público. Se isso acontecer, teste-os. Caso contrário, não os publique apenas para teste. Mesmo com variáveis e outros. Permita que as necessidades do seu aplicativo ditem o código e deixe seus testes testarem se a necessidade é atendida. (Novamente, não quero dizer testar primeiro ou não, mudar a estrutura de classes para atingir uma meta de teste).
Em vez disso, você deve "testar mais alto". Teste o método que chama o método privado. Mas seus testes devem testar suas necessidades de aplicativos e não suas "decisões de implementação".
Por exemplo (pseudo-código do bod aqui);
Não há motivo para testar "adicionar", você pode testar "livros".
Nunca deixe seus testes tomarem decisões de design de código para você. Teste se você obtém o resultado esperado, não como obtém esse resultado.
fonte
Costumo concordar que os bônus de testá-la superam os problemas de aumentar a visibilidade de alguns dos membros. Uma pequena melhoria é torná-lo protegido e virtual e substituí-lo em uma classe de teste para expô-lo.
Como alternativa, se é a funcionalidade que você deseja testar separadamente, isso não sugere um objeto ausente do seu design? Talvez você possa colocá-lo em uma classe testável separada ... então sua classe existente apenas delega para uma instância dessa nova classe.
fonte
Geralmente mantenho as classes de teste no mesmo projeto / montagem que as classes em teste.
Dessa forma, eu só preciso de
internal
visibilidade para tornar as funções / classes testáveis.Isso complica um pouco o processo de construção, que precisa filtrar as classes de teste. Consigo isso nomeando todas as minhas classes de teste
TestedClassTest
e usando um regex para filtrar essas classes.Obviamente, isso só se aplica à parte C # / .NET da sua pergunta
fonte
Eu, muitas vezes, adicionar um método chamado algo como
validate
,verify
,check
, etc, a uma classe de modo que ele pode ser chamado para testar o estado interno de um objeto.Às vezes, esse método é envolvido em um bloco ifdef (eu escrevo principalmente em C ++) para que não seja compilado para lançamento. Mas geralmente é útil na versão fornecer métodos de validação que percorrem as árvores de objetos do programa verificando as coisas.
fonte
O Goiaba possui uma anotação @VisibleForTesting para métodos de marcação com escopo ampliado (pacote ou público) que eles teriam. Eu uso uma anotação @Private para a mesma coisa.
Embora a API pública deva ser testada, às vezes é conveniente e sensato obter informações que normalmente não seriam públicas.
Quando:
parece que a religião está superando a engenharia.
fonte
Normalmente deixo esses métodos como
protected
e coloco o teste de unidade no mesmo pacote (mas em outro projeto ou pasta de origem), onde eles podem acessar todos os métodos protegidos porque o carregador de classes os colocará no mesmo espaço de nome.fonte
Não, porque existem maneiras melhores de esfolar esse gato.
Alguns chicotes de teste de unidade dependem de macros na definição de classe que se expandem automaticamente para criar ganchos quando incorporadas no modo de teste. Muito estilo C, mas funciona.
Um idioma OO mais fácil é tornar o que você deseja testar "protegido" em vez de "privado". O equipamento de teste é herdado da classe em teste e pode acessar todos os membros protegidos.
Ou você escolhe a opção "amigo". Pessoalmente, esse é o recurso do C ++ que eu menos gosto porque quebra as regras de encapsulamento, mas é necessário como o C ++ implementa alguns recursos, então, ei.
De qualquer forma, se você estiver testando a unidade, provavelmente precisará injetar valores nesses membros. Mensagens de texto em caixa branca são perfeitamente válidas. E isso realmente iria quebrar seu encapsulamento.
fonte
No .Net, há uma classe especial chamada
PrivateObject
deign especificamente para permitir que você acesse métodos privados de uma classe.Veja mais sobre isso no MSDN ou aqui no Stack Overflow
(Gostaria de saber que ninguém o mencionou até agora.)
Existem situações em que isso não é suficiente; nesse caso, você terá que usar a reflexão.
Ainda assim, eu adotaria a recomendação geral de não testar métodos privados, no entanto, como sempre, sempre há exceções.
fonte
Como é amplamente observado pelos comentários de outras pessoas, os testes de unidade devem se concentrar na API pública. No entanto, prós / contras e justificativa à parte, você pode chamar métodos privados em um teste de unidade usando reflexão. Obviamente, você precisaria garantir que sua segurança JRE o permitisse. Chamar métodos privados é algo que o Spring Framework emprega com seus ReflectionUtils (consulte o
makeAccessible(Method)
método).Aqui está uma pequena classe de exemplo com um método de instância privada.
E uma classe de exemplo que executa o método de instância privada.
A execução de B será impressa.
Doing something private.
Se você realmente precisar, a reflexão pode ser usada em testes de unidade para acessar métodos de instância privada.fonte
Pessoalmente, tenho os mesmos problemas ao testar métodos privados e isso ocorre porque algumas das ferramentas de teste são limitadas. Não é bom que seu design seja guiado por ferramentas limitadas, se elas não responderem à sua necessidade, altere a ferramenta e não o design. Como você está pedindo C #, não posso propor boas ferramentas de teste, mas para Java existem duas ferramentas poderosas: TestNG e PowerMock, e você pode encontrar as ferramentas de teste correspondentes para a plataforma .NET
fonte