Introdução ao PowerMock
1. Visão geral
O teste de unidade com a ajuda de uma estrutura de simulação foi reconhecida como uma prática útil por um longo tempo, e a estruturaMockito, em particular, dominou este mercado nos últimos anos.
E, para facilitar o design decente de códigos e simplificar a API pública, alguns recursos desejados foram intencionalmente deixados de fora. Em alguns casos, no entanto, essas deficiências forçam os testadores a escrever códigos pesados apenas para viabilizar a criação de zombarias.
É aqui que a estruturaPowerMock entra em ação.
PowerMockito é uma API de extensão do PowerMock para suportar o Mockito. Ele fornece recursos para trabalhar com a API Java Reflection de uma maneira simples para superar os problemas do Mockito, como a falta de capacidade de zombar de métodos finais, estáticos ou privados.
Este tutorial fornecerá uma introdução à API do PowerMockito e como ela é aplicada nos testes.
2. Preparando para testar com PowerMockito
A primeira etapa para integrar o suporte do PowerMock ao Mockito é incluir as duas dependências a seguir no arquivo Maven POM:
org.powermock
powermock-module-junit4
1.6.4
test
org.powermock
powermock-api-mockito
1.6.4
test
Em seguida, precisamos preparar nossos casos de teste para trabalhar comPowerMockito aplicando as duas anotações a seguir:
@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.example.powermockito.introduction.*")
O elementofullyQualifiedNames na anotação@PrepareForTest representa uma matriz de nomes totalmente qualificados de tipos que queremos simular. Nesse caso, usamos um nome de pacote com um caractere curinga para dizer aPowerMockito para preparar todos os tipos dentro do pacotecom.example.powermockito.introduction para simulação.
Agora estamos prontos para explorar o poder dePowerMockito.
3. Construtores de simulação e métodos finais
Nesta seção, demonstraremos as maneiras de obter uma instância simulada em vez de uma real ao instanciar uma classe com o operadornew, e então usar esse objeto para simular um método final. A classe colaboradora, cujos construtores e métodos finais serão ridicularizados, é definida da seguinte maneira:
public class CollaboratorWithFinalMethods {
public final String helloMethod() {
return "Hello World!";
}
}
Primeiro, criamos um objeto simulado usando a APIPowerMockito:
CollaboratorWithFinalMethods mock = mock(CollaboratorWithFinalMethods.class);
Em seguida, defina uma expectativa dizendo que sempre que o construtor no-arg dessa classe for chamado, uma instância simulada deve ser retornada em vez de real:
whenNew(CollaboratorWithFinalMethods.class).withNoArguments().thenReturn(mock);
Vamos ver como essa simulação de construção funciona em ação instanciando a classeCollaboratorWithFinalMethods usando seu construtor padrão e, em seguida, verificar os comportamentos do PowerMock:
CollaboratorWithFinalMethods collaborator = new CollaboratorWithFinalMethods();
verifyNew(CollaboratorWithFinalMethods.class).withNoArguments();
Na próxima etapa, uma expectativa é definida para o método final:
when(collaborator.helloMethod()).thenReturn("Hello example!");
Este método é então executado:
String welcome = collaborator.helloMethod();
As seguintes afirmações confirmam que o métodohelloMethod foi chamado no objetocollaborator e retorna o valor definido pela expectativa de simulação:
Mockito.verify(collaborator).helloMethod();
assertEquals("Hello example!", welcome);
Se quisermos simular um método final específico em vez de todos os finais dentro de um objeto, o métodoMockito.spy(T object) pode ser útil. Isso é ilustrado na seção 5.
4. Zombar de métodos estáticos
Suponha que desejemos simular métodos estáticos de uma classe chamadaCollaboratorWithStaticMethods.. Esta classe é declarada da seguinte maneira:
public class CollaboratorWithStaticMethods {
public static String firstMethod(String name) {
return "Hello " + name + " !";
}
public static String secondMethod() {
return "Hello no one!";
}
public static String thirdMethod() {
return "Hello no one again!";
}
}
Para simular esses métodos estáticos, precisamos registrar a classe envolvente com a APIPowerMockito:
mockStatic(CollaboratorWithStaticMethods.class);
Como alternativa, podemos usar o métodoMockito.spy(Class<T> class) para simular um método específico, conforme demonstrado na seção a seguir.
Em seguida, as expectativas podem ser definidas para definir os valores que os métodos devem retornar quando chamados:
when(CollaboratorWithStaticMethods.firstMethod(Mockito.anyString()))
.thenReturn("Hello example!");
when(CollaboratorWithStaticMethods.secondMethod()).thenReturn("Nothing special");
Ou uma exceção pode ser definida para ser lançada ao chamar o métodothirdMethod:
doThrow(new RuntimeException()).when(CollaboratorWithStaticMethods.class);
CollaboratorWithStaticMethods.thirdMethod();
Agora, é hora de executar os dois primeiros métodos:
String firstWelcome = CollaboratorWithStaticMethods.firstMethod("Whoever");
String secondWelcome = CollaboratorWithStaticMethods.firstMethod("Whatever");
Em vez de chamar membros da classe real, as invocações acima são delegadas aos métodos do mock. As seguintes afirmações provam que a simulação entrou em vigor:
assertEquals("Hello example!", firstWelcome);
assertEquals("Hello example!", secondWelcome);
Também podemos verificar o comportamento dos métodos do mock, incluindo quantas vezes um método é invocado. Nesse caso, ofirstMethod foi chamado duas vezes, enquanto osecondMethod nunca:
verifyStatic(Mockito.times(2));
CollaboratorWithStaticMethods.firstMethod(Mockito.anyString());
verifyStatic(Mockito.never());
CollaboratorWithStaticMethods.secondMethod();
Note: O métodoverifyStatic deve ser chamado imediatamente antes de qualquer verificação de método estático paraPowerMockito para saber que a invocação sucessiva do método é o que precisa ser verificado.
Por último, o métodothirdMethod estático deve lançar umRuntimeException conforme declarado no mock anterior. É validado pelo elementoexpected da anotação@Test:
@Test(expected = RuntimeException.class)
public void givenStaticMethods_whenUsingPowerMockito_thenCorrect() {
// other methods
CollaboratorWithStaticMethods.thirdMethod();
}
5. Zombaria parcial
Em vez de simular uma classe inteira, a APIPowerMockito permite simular parte dela usando o métodospy. A classe a seguir será usada como colaborador para ilustrar o suporte do PowerMock para zombaria parcial:
public class CollaboratorForPartialMocking {
public static String staticMethod() {
return "Hello example!";
}
public final String finalMethod() {
return "Hello example!";
}
private String privateMethod() {
return "Hello example!";
}
public String privateMethodCaller() {
return privateMethod() + " Welcome to the Java world.";
}
}
Vamos começar simulando um método estático, que é denominadostaticMethod na definição de classe acima. Primeiro, use a APIPowerMockito para simular parcialmente a classeCollaboratorForPartialMocking e definir uma expectativa para seu método estático:
spy(CollaboratorForPartialMocking.class);
when(CollaboratorForPartialMocking.staticMethod()).thenReturn("I am a static mock method.");
O método estático é então executado:
returnValue = CollaboratorForPartialMocking.staticMethod();
O comportamento de zombaria é verificado da seguinte maneira:
verifyStatic();
CollaboratorForPartialMocking.staticMethod();
A afirmação a seguir confirma que o método simulado foi realmente chamado comparando o valor de retorno com a expectativa:
assertEquals("I am a static mock method.", returnValue);
Agora é hora de passar para os métodos final e privado. Para ilustrar a simulação parcial desses métodos, precisamos instanciar a classe e informar a APIPowerMockito paraspy:
CollaboratorForPartialMocking collaborator = new CollaboratorForPartialMocking();
CollaboratorForPartialMocking mock = spy(collaborator);
Os objetos criados acima são usados para demonstrar a zombaria dos métodos final e privado. Lidaremos com o método final agora, definindo uma expectativa e chamando o método:
when(mock.finalMethod()).thenReturn("I am a final mock method.");
returnValue = mock.finalMethod();
O comportamento de zombar parcialmente desse método é comprovado:
Mockito.verify(mock).finalMethod();
Um teste verifica se a chamada do métodofinalMethod retornará um valor que corresponde à expectativa:
assertEquals("I am a final mock method.", returnValue);
Um processo semelhante é aplicado ao método privado. A principal diferença é que não podemos invocar diretamente esse método a partir do caso de teste. Basicamente, um método privado deve ser chamado por outros da mesma classe. Na classeCollaboratorForPartialMocking, o métodoprivateMethod deve ser invocado pelo métodoprivateMethodCaller e usaremos o último como um delegado. Vamos começar com a expectativa e invocação:
when(mock, "privateMethod").thenReturn("I am a private mock method.");
returnValue = mock.privateMethodCaller();
A zombaria do método privado é confirmada:
verifyPrivate(mock).invoke("privateMethod");
O teste a seguir garante que o valor de retorno da invocação do método privado seja o mesmo que a expectativa:
assertEquals("I am a private mock method. Welcome to the Java world.", returnValue);
6. Conclusão
Este tutorial forneceu uma introdução à APIPowerMockito, demonstrando seu uso na solução de alguns dos problemas que os desenvolvedores encontram ao usar a estrutura Mockito.
A implementação desses exemplos e trechos de código pode ser encontrada emthe linked GitHub project.