Como faço para testar a unidade (usando xUnit) uma classe que possui métodos privados, campos ou classes aninhadas? Ou uma função que é tornada privada por ter ligação interna ( static
em C / C ++) ou está em um espaço para nome privado ( anônimo )?
Parece ruim alterar o modificador de acesso de um método ou função apenas para poder executar um teste.
java
unit-testing
tdd
Raedwald
fonte
fonte
Respostas:
Se você possui um aplicativo Java herdado e não tem permissão para alterar a visibilidade de seus métodos, a melhor maneira de testar métodos privados é usar a reflexão .
Internamente, estamos usando ajudantes para obter / definir
private
eprivate static
variáveis, além de invocarprivate
eprivate static
métodos. Os seguintes padrões permitem fazer praticamente qualquer coisa relacionada aos métodos e campos privados. Obviamente, você não pode alterarprivate static final
variáveis através da reflexão.E para campos:
fonte
A melhor maneira de testar um método privado é através de outro método público. Se isso não puder ser feito, uma das seguintes condições será verdadeira:
fonte
Quando tenho métodos particulares em uma classe que são suficientemente complicados, sinto a necessidade de testar diretamente os métodos privados, isso é um cheiro de código: minha classe é muito complicada.
Minha abordagem usual para resolver esses problemas é provocar uma nova classe que contém os bits interessantes. Frequentemente, esse método e os campos com os quais interage, e talvez outro método ou dois possam ser extraídos para uma nova classe.
A nova classe expõe esses métodos como 'públicos', para que sejam acessíveis para testes de unidade. As classes novas e antigas agora são mais simples que a classe original, o que é ótimo para mim (eu preciso manter as coisas simples ou me perco!).
Note que não estou sugerindo que as pessoas criem classes sem usar o cérebro! O objetivo aqui é usar as forças do teste de unidade para ajudá-lo a encontrar boas novas classes.
fonte
Eu usei a reflexão para fazer isso no Java no passado e, na minha opinião, foi um grande erro.
A rigor, você não deve escrever testes de unidade que testem diretamente métodos privados. O que você deve testar é o contrato público que a classe possui com outros objetos; você nunca deve testar diretamente as partes internas de um objeto. Se outro desenvolvedor quiser fazer uma pequena alteração interna na classe, o que não afeta o contrato público da classe, ele deverá modificar seu teste baseado em reflexão para garantir que funcione. Se você fizer isso repetidamente em um projeto, os testes de unidade deixarão de ser uma medida útil da integridade do código e começarão a se tornar um obstáculo ao desenvolvimento e um aborrecimento para a equipe de desenvolvimento.
O que eu recomendo fazer é usar uma ferramenta de cobertura de código como Cobertura, para garantir que os testes de unidade que você escreve forneçam uma cobertura decente do código em métodos privados. Dessa forma, você indiretamente testa o que os métodos privados estão fazendo e mantém um nível mais alto de agilidade.
fonte
Neste artigo: Testando métodos privados com JUnit e SuiteRunner (Bill Venners), você basicamente tem 4 opções:
fonte
Geralmente, um teste de unidade destina-se a exercer a interface pública de uma classe ou unidade. Portanto, métodos privados são detalhes de implementação que você não esperaria testar explicitamente.
fonte
Apenas dois exemplos de onde eu gostaria de testar um método privado:
SecurityManager
não está configurada para evitar isso).Entendo a ideia de apenas testar o "contrato". Mas não vejo como alguém possa advogar na verdade não testar o código - sua milhagem pode variar.
Portanto, minha troca envolve complicar as JUnits com reflexão, em vez de comprometer minha segurança e SDK.
fonte
private
não é a única alternativa. Nenhum modificador de acesso épackage private
e significa que você pode testá-lo enquanto o seu teste estiver no mesmo pacote.Os métodos privados são chamados por um método público, portanto, as entradas para seus métodos públicos também devem testar métodos privados chamados por esses métodos públicos. Quando um método público falha, isso pode ser uma falha no método privado.
fonte
Outra abordagem que usei é alterar um método privado para empacotar privado ou protegido e complementá-lo com o @VisibleForTesting anotação da biblioteca do Google Guava.
Isso instruirá qualquer pessoa que use esse método a tomar cuidado e não acessá-lo diretamente, mesmo em um pacote. Também uma classe de teste não precisa estar no mesmo pacote fisicamente , mas no mesmo pacote sob o teste pasta de .
Por exemplo, se um método a ser testado estiver presente
src/main/java/mypackage/MyClass.java
, sua chamada de teste deverá ser efetuadasrc/test/java/mypackage/MyClassTest.java
. Dessa forma, você tem acesso ao método de teste em sua classe de teste.fonte
Para testar código legado com classes grandes e peculiares, geralmente é muito útil poder testar o único método privado (ou público) que estou escrevendo agora .
Eu uso o pacote junitx.util.PrivateAccessor para Java. Muitas linhas de ajuda úteis para acessar métodos e campos particulares.
fonte
Class
como primeiro parâmetro nesses métodos, você esteja acessando apenasstatic
membros.No Spring Framework, você pode testar métodos privados usando este método:
Por exemplo:
fonte
Tendo experimentado a solução da Cem Catikkas usando reflexão para Java, eu diria que a solução dele era mais elegante do que descrevi aqui. No entanto, se você estiver procurando uma alternativa ao uso da reflexão e tiver acesso à fonte que está testando, isso ainda será uma opção.
Existe mérito possível ao testar métodos particulares de uma classe, principalmente no desenvolvimento orientado a testes , no qual você deseja projetar pequenos testes antes de escrever qualquer código.
A criação de um teste com acesso a membros e métodos privados pode testar áreas de código difíceis de atingir especificamente com acesso apenas a métodos públicos. Se um método público tiver várias etapas envolvidas, ele poderá consistir em vários métodos particulares, que poderão ser testados individualmente.
Vantagens:
Desvantagens:
No entanto, se o teste contínuo exigir esse método, pode ser um sinal de que os métodos privados devem ser extraídos, o que pode ser testado da maneira tradicional e pública.
Aqui está um exemplo complicado de como isso funcionaria:
A classe interna seria compilada para
ClassToTest$StaticInnerTest
.Veja também: Dica Java 106: Classes internas estáticas para diversão e lucro
fonte
Como outros já disseram ... não teste métodos privados diretamente. Aqui estão alguns pensamentos:
Execute a cobertura do código nos testes de unidade. Se você perceber que os métodos não foram totalmente testados, adicione-os aos testes para aumentar a cobertura. Procure 100% de cobertura do código, mas saiba que você provavelmente não conseguirá.
fonte
Métodos privados são consumidos por métodos públicos. Caso contrário, eles são código morto. É por isso que você testa o método público, afirmando os resultados esperados do método público e, portanto, os métodos privados que ele consome.
O teste de métodos particulares deve ser testado por depuração antes de executar seus testes de unidade em métodos públicos.
Eles também podem ser depurados usando desenvolvimento orientado a testes, depurando seus testes de unidade até que todas as suas asserções sejam atendidas.
Pessoalmente, acredito que é melhor criar classes usando TDD; criando os stubs de método público e, em seguida, gerando testes de unidade com todas as asserções definidas anteriormente, para que o resultado esperado do método seja determinado antes da codificação. Dessa forma, você não segue o caminho errado de fazer com que as asserções de teste de unidade se ajustem aos resultados. Sua classe é robusta e atende aos requisitos quando todos os seus testes de unidade passam.
fonte
Se você estiver usando o Spring, o ReflectionTestUtils fornece algumas ferramentas úteis que ajudam aqui com o mínimo esforço. Por exemplo, para configurar uma simulação em um membro privado sem ser forçado a adicionar um configurador público indesejável:
fonte
Se você está tentando testar o código existente que não está disposto a mudar, a reflexão é uma boa escolha.
Se o design da classe ainda for flexível e você tiver um método privado complicado que você deseja testar separadamente, sugiro que você o puxe para uma classe separada e teste essa classe separadamente. Isso não precisa alterar a interface pública da classe original; ele pode criar internamente uma instância da classe auxiliar e chamar o método auxiliar.
Se você deseja testar condições de erro difíceis provenientes do método auxiliar, pode dar um passo adiante. Extraia uma interface da classe auxiliar, adicione um getter e setter público à classe original para injetar a classe auxiliar (usada por meio de sua interface) e, em seguida, injete uma versão simulada da classe auxiliar na classe original para testar como a classe original responde a exceções do ajudante. Essa abordagem também é útil se você quiser testar a classe original sem também testar a classe auxiliar.
fonte
O teste de métodos particulares interrompe o encapsulamento de sua classe, pois toda vez que você altera a implementação interna, o código do cliente é interrompido (nesse caso, os testes).
Portanto, não teste métodos particulares.
fonte
A resposta da página de perguntas frequentes do JUnit.org :
PS: Eu sou o principal colaborador do Dp4j , pergunte- me se você precisar de ajuda. :)
fonte
Se você deseja testar métodos particulares de um aplicativo herdado em que não pode alterar o código, uma opção para Java é o jMockit , que permitirá criar simulações para um objeto, mesmo quando elas são privadas para a classe.
fonte
Deencapsulation.invoke(instance, "privateMethod", param1, param2);
Costumo não testar métodos particulares. Aí está a loucura. Pessoalmente, acredito que você só deve testar suas interfaces expostas publicamente (e isso inclui métodos internos e protegidos).
fonte
Se você estiver usando o JUnit, consulte os junit-addons . Ele tem a capacidade de ignorar o modelo de segurança Java e acessar métodos e atributos privados.
fonte
Eu sugiro que você refatorar seu código um pouco. Quando você começa a pensar em usar reflexão ou outro tipo de coisa, apenas para testar seu código, algo está errado com seu código.
Você mencionou diferentes tipos de problemas. Vamos começar com campos particulares. No caso de campos particulares, eu teria adicionado um novo construtor e injetado campos nele. Em vez disso:
Eu teria usado isso:
Isso não será um problema, mesmo com algum código legado. O código antigo usará um construtor vazio e, se você me perguntar, o código refatorado parecerá mais limpo e você poderá injetar os valores necessários no teste sem reflexão.
Agora sobre métodos privados. Na minha experiência pessoal, quando você precisa stubar um método privado para teste, esse método não tem nada a ver nessa classe. Um padrão comum, nesse caso, seria envolvê- lo em uma interface, como
Callable
e você passará nessa interface também no construtor (com esse truque de vários construtores):Principalmente tudo o que escrevi parece ser um padrão de injeção de dependência. Na minha experiência pessoal, é realmente útil durante os testes, e acho que esse tipo de código é mais limpo e será mais fácil de manter. Eu diria o mesmo sobre classes aninhadas. Se uma classe aninhada contiver lógica pesada, seria melhor se você a tivesse movido como uma classe privada de pacote e a injetado em uma classe que precisa.
Também existem vários outros padrões de design que eu usei ao refatorar e manter o código legado, mas tudo depende dos casos do seu código para testar. Usar a reflexão principalmente não é um problema, mas quando você tem um aplicativo corporativo que é fortemente testado e os testes são executados antes de cada implantação, tudo fica realmente lento (é irritante e eu não gosto desse tipo de coisa).
Também há injeção de setter, mas eu não recomendaria usá-lo. É melhor ficar com um construtor e inicializar tudo quando for realmente necessário, deixando a possibilidade de injetar dependências necessárias.
fonte
ClassToTest(Callable)
injeção. Isso tornaClassToTest
mais complicado. Mantenha simples. Além disso, isso exige que outra pessoa conteClassToTest
algo queClassToTest
deve ser o chefe. Há um lugar para injetar lógica, mas não é isso. Você acabou de tornar a classe mais difícil de manter, não mais fácil.X
não aumente aX
complexidade a ponto de causar mais problemas ... e, portanto, exigir testes adicionais ... que se você tiver implementado de uma maneira que possa causar mais problemas .. . (este não é um loops infinitos; cada iteração é provável menor do que o anterior, mas ainda irritante)ClassToTest
mais difícil de manter? Na verdade, ele facilita a manutenção do aplicativo. O que você sugere para criar uma nova classe para cada valor diferente que você precisafirst
e na 'segunda' variável?class A{ A(){} f(){} }
é mais simples queclass A { Logic f; A(logic_f) } class B { g() { new A( logic_f) } }
. Se isso não fosse verdade, e se fosse mais fácil manter a lógica de uma classe como argumentos construtores, passaríamos toda a lógica como argumentos construtores. Eu simplesmente não vejo como você pode reivindicarclass B{ g() { new A( you_dont_know_your_own_logic_but_I_do ) } }
que torna A mais fácil de manter. Há casos em que injetáveis como este sentido marcas, mas eu não ver o seu exemplo como "mais fácil de manter"Um método privado deve ser acessado apenas na mesma classe. Portanto, não há como testar um método "privado" de uma classe de destino de qualquer classe de teste. Uma saída é que você pode executar o teste de unidade manualmente ou pode mudar seu método de "privado" para "protegido".
E, em seguida, um método protegido pode ser acessado apenas dentro do mesmo pacote em que a classe está definida. Portanto, testar um método protegido de uma classe de destino significa que precisamos definir sua classe de teste no mesmo pacote que a classe de destino.
Se todas as opções acima não atenderem aos seus requisitos, use a maneira de reflexão para acessar o método privado.
fonte
Como muitos sugeriram acima, uma boa maneira é testá-los por meio de suas interfaces públicas.
Se você fizer isso, é uma boa ideia usar uma ferramenta de cobertura de código (como Emma) para ver se seus métodos privados estão de fato sendo executados a partir de seus testes.
fonte
Aqui está minha função genérica para testar campos particulares:
fonte
Por favor, veja abaixo um exemplo;
A seguinte declaração de importação deve ser adicionada:
Agora você pode transmitir diretamente o objeto que possui o método privado, o nome do método a ser chamado e parâmetros adicionais, como abaixo.
fonte
Hoje, instalei uma biblioteca Java para ajudar a testar métodos e campos privados. Ele foi projetado com o Android em mente, mas pode realmente ser usado para qualquer projeto Java.
Se você obteve algum código com métodos, campos ou construtores particulares, pode usar o BoundBox . Faz exatamente o que você está procurando. Aqui está um exemplo de teste que acessa dois campos particulares de uma atividade do Android para testá-la:
O BoundBox facilita o teste de campos, métodos e construtores privados / protegidos. Você pode até acessar coisas ocultas por herança. De fato, o BoundBox quebra o encapsulamento. Isso lhe dará acesso a tudo isso através da reflexão, MAS tudo será verificado no momento da compilação.
É ideal para testar algum código legado. Use-o com cuidado. ;)
https://github.com/stephanenicolas/boundbox
fonte
Primeiro, jogarei essa pergunta fora: Por que seus membros privados precisam de testes isolados? Eles são tão complexos, fornecendo comportamentos tão complicados que exigem testes separados da superfície pública? É um teste de unidade, não um teste de 'linha de código'. Não se preocupe com as pequenas coisas.
Se eles são tão grandes, grandes o suficiente para que esses membros privados sejam uma 'unidade' grande em complexidade - considere refatorar esses membros privados dessa classe.
Se a refatoração for inadequada ou inviável, você pode usar o padrão de estratégia para substituir o acesso a essas funções-membro privadas / classes-membro quando estiver em teste de unidade? Sob teste de unidade, a estratégia forneceria validação adicional, mas nas versões lançadas seria uma passagem simples.
fonte
Recentemente, tive esse problema e escrevi uma pequena ferramenta, chamada Picklock , que evita os problemas de usar explicitamente a API de reflexão Java, dois exemplos:
Métodos de chamada, por exemplo
private void method(String s)
- por reflexão JavaMétodos de chamada, por exemplo
private void method(String s)
- por PicklockDefinir campos, por exemplo
private BigInteger amount;
- por reflexão JavaDefinir campos, por exemplo
private BigInteger amount;
- por Picklockfonte
PowerMockito é feito para isso. Usar dependência maven
Então você pode fazer
fonte