Como você testa métodos privados?

166

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?

    
por Vinoth Kumar C M 14.08.2011 / 06:44
fonte

10 respostas

224

Você geralmente não testa os métodos privados diretamente. Como são privados, considere-os um detalhe de implementação. Ninguém vai ligar para um deles e esperar que ele funcione de uma maneira particular.

Você deve testar sua interface pública. Se os métodos que chamam seus métodos privados estiverem funcionando conforme o esperado, você supõe, por extensão, que seus métodos privados estão funcionando corretamente.

    
por 14.08.2011 / 07:40
fonte
110

Em geral, eu evitaria isso. Se o seu método privado é tão complexo que precisa de um teste de unidade separado, isso geralmente significa que ele merecia sua própria classe. Isso pode encorajá-lo a escrevê-lo de uma maneira que seja reutilizável. Você deve então testar a nova classe e chamar a interface pública dela em sua classe antiga.

Por outro lado, às vezes separar os detalhes da implementação em classes separadas leva a classes com interfaces complexas, muitos dados passando entre as classes antiga e nova, ou a um design que pode parecer bom do ponto de vista da POO, mas não combina com as intuições vindas do domínio do problema (por exemplo, dividir um modelo de precificação em duas partes apenas para evitar testar métodos privados 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 são sempre alteradas juntas.

Quando confrontado com uma escolha entre o 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 OOP que não funciona corretamente, porque não foi testado adequadamente. Em Java, você pode simplesmente dar acesso ao método "padrão" e colocar o teste unitário no mesmo pacote. 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 da implementação requer um novo teste do código e, se os testes precisarem ser modificados para fazer isso, você só precisa fazer isso. isso.

Em geral, uma classe pode oferecer 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 adequadamente testado. Não precisa ser um teste de unidade em um método privado - pode ser, por exemplo, registro em log. O registro também "interrompe o encapsulamento", mas ainda o fazemos, porque é muito útil.

    
por 14.08.2011 / 10:18
fonte
26

O teste de métodos privados dependeria de sua complexidade; alguns métodos privados de uma linha realmente não justificam o esforço extra de testes (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 private private package, que permitirá o acesso a um teste unitário no mesmo pacote, mas ele ainda será encapsulado em todos os outros códigos. Isso dará a vantagem de testar a lógica do método privado diretamente, em vez de depender de um teste de método público para cobrir todas as partes da lógica (possivelmente) complexa.

Se estiver emparelhado com a anotação @Visiblestrongsting na biblioteca do Google Guava, você estará marcando claramente o método privado desse pacote como visível apenas para teste e, como tal, não deverá ser chamado por nenhuma outra classe.

Os oponentes dessa técnica argumentam que isso quebrará o encapsulamento e abrirá métodos privados para codificar no mesmo pacote. Embora eu concorde que isso rompa o encapsulamento e abra código privado para outras classes, eu argumento que testar lógica complexa é mais importante que o encapsulamento strict e não usar métodos privados de pacotes que estão claramente marcados como visível apenas para teste deve ser de responsabilidade dos desenvolvedores usando e alterando a base de código.

Método privado antes de testar:

private int add(int a, int b){
    return a + b;
}

Método privado 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 seu código principal e código de teste em estruturas de pastas físicas separadas é uma boa prática em geral, mas essa técnica funcionará enquanto as classes estiverem definidas no mesmo pacote.

    
por 27.11.2012 / 11:43
fonte
14

Se você não puder usar APIs externas ou não quiser, ainda poderá 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 link ou a API Java do Java Tutorial link 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.

    
por 31.01.2014 / 13:10
fonte
9

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, em seguida, tentar depurar que parte não estava funcionando. Portanto, o caso de teste de unidade deve ser testado, independentemente do seu escopo. A seguir, uma maneira de testar métodos privados.

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 wrapper em torno dele. ( link )

    
por 05.02.2014 / 04:07
fonte
8

Antes de tudo, como outros autores sugeriram: pense duas vezes se você realmente precisa testar um método privado. E se sim, ...

No .NET, você pode convertê-lo no método "Interno" e criar um pacote " InternalVisible " para o seu projeto de teste de unidade.

Em Java, você pode escrever testes em si na classe a ser testada e seus métodos de teste devem poder chamar métodos privados também. Eu realmente não tenho grande experiência com Java, então essa provavelmente não é a melhor prática.

Obrigado.

    
por 12.04.2012 / 20:19
fonte
7

Eu costumo fazer tais 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 em unidade (o JUnit ou o TestNG realmente não importam), mas você mantém esses métodos de quem chama não deseja.

Observe que isso espera uma árvore de fontes no estilo de especialista.

    
por 13.12.2013 / 11:52
fonte
3

Se você realmente precisa testar um método privado, com Java, você pode usar fest assert e / ou fest reflect . Ele usa reflexão.

Importe a biblioteca com o maven (as versões fornecidas não são as mais recentes) ou importe-as 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>

Como exemplo, se você tem uma classe chamada 'MyClass' com um método privado chamado 'myPrivateMethod', que usa um String como parâmetro para atualizar seu valor para 'this is cool testing!', você pode fazer o seguinte teste de junit :

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);
}

Esta biblioteca também permite que você substitua quaisquer propriedades do bean (não importa se elas são particulares e se nenhum setter foi gravado) por uma simulação, e usar isso com o Mockito ou qualquer outra estrutura simulada é 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.

    
por 27.11.2012 / 10:45
fonte
2

O que normalmente faço em C # é tornar meus métodos protegidos e não privados. É 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 mesmo. No meu código de teste, posso criar uma classe de teste especial que amplia 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();
   }
}

Esta classe existe apenas no meu projeto de teste. Seu único objetivo é fornecer acesso a esse método único. Isso me permite acessar lugares que a maioria das outras classes não tem.

    
por 09.05.2013 / 11:16
fonte
0

Você pode testar métodos privados com facilidade se colocar seus testes de unidade em uma classe interna da classe que está testando. Usando TestNG seus testes unitários 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");
    }
}

Como é 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ê quer apenas testar uma classe, você pode fazer

$ mvn test -Dtest=*UserEditorTest

Fonte: link

    
por 10.04.2013 / 22:44
fonte