Estou trabalhando em um projeto java. Eu sou novo no teste de unidade. Qual é a melhor maneira de testar métodos privados em classes java?
Estou trabalhando em um projeto java. Eu sou novo no teste de unidade. Qual é a melhor maneira de testar métodos privados em classes java?
Respostas:
Geralmente, você não realiza testes particulares de unidade diretamente. Como eles são privados, considere-os um detalhe de implementação. Ninguém nunca ligará para um deles e espera que funcione de uma maneira particular.
Em vez disso, você deve testar sua interface pública. Se os métodos que chamam seus métodos particulares estiverem funcionando conforme o esperado, você supõe, por extensão, que seus métodos particulares estão funcionando corretamente.
Em geral, eu evitaria isso. Se o seu método privado é tão complexo que precisa de um teste de unidade separado, geralmente significa que ele merecia sua própria classe. Isso pode encorajá-lo a escrevê-lo de uma maneira reutilizável. Você deve testar a nova classe e chamar a interface pública dela na sua classe antiga.
Por outro lado, às vezes fatorar os detalhes da implementação em classes separadas leva a classes com interfaces complexas, muitos dados passando entre a antiga e a nova classe ou a um design que pode parecer bom do ponto de vista da OOP, mas não corresponder às intuições provenientes do domínio do problema (por exemplo, dividir um modelo de precificação em duas partes, apenas para evitar testar métodos particulares, não é muito intuitivo e pode levar a problemas mais tarde ao manter / estender o código). Você não quer ter "classes gêmeas" que sempre são alteradas juntas.
Quando confrontado com uma escolha entre encapsulamento e testabilidade, prefiro ir para o segundo. É mais importante ter o código correto (ou seja, produzir a saída correta) do que um bom design de POO que não funcione corretamente, porque não foi testado adequadamente. Em Java, você pode simplesmente acessar o método "padrão" e colocar o teste de unidade no mesmo pacote. Os testes de unidade são simplesmente parte do pacote que você está desenvolvendo e não há problema em ter uma dependência entre os testes e o código que está sendo testado. Isso significa que, quando você altera a implementação, pode ser necessário alterar seus testes, mas tudo bem - cada alteração na implementação requer um novo teste do código, e se os testes precisam ser modificados para isso, basta fazer isto.
Em geral, uma classe pode estar oferecendo mais de uma interface. Existe uma interface para os usuários e uma interface para os mantenedores. O segundo pode expor mais para garantir que o código seja testado adequadamente. Não precisa ser um teste de unidade em um método privado - pode ser, por exemplo, registro em log. O registro também "quebra o encapsulamento", mas ainda o fazemos, porque é muito útil.
O teste de métodos privados dependeria de sua complexidade; alguns métodos privados de uma linha realmente não justificariam o esforço extra de teste (isso também pode ser dito de métodos públicos), mas alguns métodos privados podem ser tão complexos quanto os métodos públicos e difíceis de testar através da interface pública.
Minha técnica preferida é tornar o pacote de método privado privado, o que permitirá o acesso a um teste de unidade no mesmo pacote, mas ele ainda será encapsulado de todos os outros códigos. Isso dará a vantagem de testar diretamente a lógica do método privado, em vez de precisar confiar em um teste de método público para cobrir todas as partes da lógica (possivelmente) complexa.
Se isso estiver associado à anotação @VisibleForTesting na biblioteca do Google Guava, você está claramente marcando este método privado de pacote como visível apenas para teste e, como tal, não deve ser chamado por nenhuma outra classe.
Os opositores dessa técnica argumentam que isso quebrará o encapsulamento e abrirá métodos privados para codificar no mesmo pacote. Embora eu concorde que isso quebra o encapsulamento e abra o código privado para outras classes, eu argumento que testar lógica complexa é mais importante que o encapsulamento estrito e não usar métodos privados de pacote que são claramente marcados como visíveis apenas para teste devem ser de responsabilidade dos desenvolvedores usando e alterando a base de código.
Método privado antes do teste:
private int add(int a, int b){
return a + b;
}
Método particular do pacote pronto para teste:
@VisibleForTesting
int add(int a, int b){
return a + b;
}
Nota: Colocar testes no mesmo pacote não é equivalente a colocá-los na mesma pasta física. Separar o código principal e o código de teste em estruturas de pastas físicas separadas é uma boa prática em geral, mas essa técnica funcionará desde que as classes sejam definidas como no mesmo pacote.
Se você não pode usar APIs externas ou não deseja, ainda pode usar a API JDK padrão pura para acessar métodos privados usando reflexão. Aqui está um exemplo
MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);
Verifique o Java Tutorial http://docs.oracle.com/javase/tutorial/reflect/ ou a API Java http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary. html para mais informações.
Como @kij citou em sua resposta, há momentos em que uma solução simples usando reflexão é realmente boa para testar um método privado.
Caso de teste de unidade significa testar a unidade de código. Isso não significa testar a interface, porque se você estiver testando a interface, isso não significa que você está testando a unidade de código. Torna-se uma espécie de teste de caixa preta. Além disso, é melhor encontrar problemas no menor nível de unidade do que determinar os problemas no nível da interface e depois tentar depurar o componente que não estava funcionando. Portanto, o caso de teste de unidade deve ser testado independentemente de seu escopo. A seguir, é uma maneira de testar métodos particulares.
Se você estiver usando java, poderá usar o jmockit, que fornece o Deencapsulation.invoke para chamar qualquer método privado da classe em teste. Ele usa reflexão para chamá-lo eventualmente, mas fornece um bom invólucro ao seu redor. ( https://code.google.com/p/jmockit/ )
Primeiro de tudo, como outros autores sugeriram: pense duas vezes se você realmente precisa testar o método privado. E se, ...
No .NET, você pode convertê-lo no método "Interno" e tornar o pacote " InternalVisible " no seu projeto de teste de unidade.
Em Java, você pode escrever os próprios testes na classe a ser testada e seus métodos de teste também devem poder chamar métodos privados. Eu realmente não tenho grande experiência em Java, portanto essa provavelmente não é a melhor prática.
Obrigado.
Eu costumo fazer esses métodos protegidos. Digamos que sua turma esteja em:
src/main/java/you/Class.java
Você pode criar uma classe de teste como:
src/test/java/you/TestClass.java
Agora você tem acesso a métodos protegidos e pode testá-los unitariamente (JUnit ou TestNG realmente não importa), mas mantém esses métodos longe dos chamadores que você não queria.
Observe que isso espera uma árvore de fontes no estilo maven.
Se você realmente precisa testar o método privado, com Java, quero dizer, você pode usar fest assert
e / ou fest reflect
. Ele usa reflexão.
Importe a biblioteca com o maven (determinadas versões não são as mais recentes que eu acho) ou importe-a diretamente no seu caminho de classe:
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.2</version>
</dependency>
Por exemplo, se você tiver uma classe chamada 'MyClass' com um método privado chamado 'myPrivateMethod', que usa uma String como parâmetro e atualiza seu valor para 'this is cool testing!', Você pode executar o seguinte teste de junção:
import static org.fest.reflect.core.Reflection.method;
...
MyClass objectToTest;
@Before
public void setUp(){
objectToTest = new MyClass();
}
@Test
public void testPrivateMethod(){
// GIVEN
String myOriginalString = "toto";
// WHEN
method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
// THEN
Assert.assertEquals("this is cool testing !", myOriginalString);
}
Essa biblioteca também permite que você substitua quaisquer propriedades de bean (não importa se são particulares e nenhum setter é gravado) por um mock, e usá-lo com o Mockito ou qualquer outra estrutura de mock é muito legal. A única coisa que você precisa saber no momento (não sei se isso será melhor nas próximas versões) é o nome do campo / método de destino que você deseja manipular e sua assinatura.
O que normalmente faço em C # é tornar meus métodos protegidos e não particulares. É um modificador de acesso um pouco menos privado, mas oculta o método de todas as classes que não herdam da classe em teste.
public class classUnderTest
{
//this would normally be private
protected void methodToTest()
{
//some code here
}
}
Qualquer classe que não herda diretamente de classUnderTest não tem idéia de que methodToTest existe. No meu código de teste, posso criar uma classe de teste especial que estende e fornece acesso a esse método ...
class TestingClass : classUnderTest
{
public void methodToTest()
{
//this returns the method i would like to test
return base.methodToTest();
}
}
Essa classe existe apenas no meu projeto de teste. Seu único objetivo é fornecer acesso a esse método único. Permite-me acessar lugares que a maioria das outras classes não possui.
protected
faz parte da API pública e incorre nas mesmas limitações (nunca pode ser alterado, deve ser documentado, ...). Em C #, você pode usar internal
junto com InternalsVisibleTo
.
Você pode testar métodos privados facilmente se colocar seus testes de unidade em uma classe interna na classe que está testando. Usando TestNG, seus testes de unidade devem ser classes internas estáticas públicas anotadas com @Test, assim:
@Test
public static class UserEditorTest {
public void test_private_method() {
assertEquals(new UserEditor().somePrivateMethod(), "success");
}
}
Por ser uma classe interna, o método privado pode ser chamado.
Meus testes são executados no maven e ele encontra automaticamente esses casos de teste. Se você quiser apenas testar uma classe, pode fazer
$ mvn test -Dtest=*UserEditorTest
Fonte: http://www.ninthavenue.com.au/how-to-unit-test-private-methods