Mock de métodos estáticos em Java

Logo PowerMock

A utilização de métodos estáticos, na maioria dos casos, deve ser evitada por dificultar os testes unitários e criar um alto acoplamento. Mas se você se deparar com um método estático e tiver que sobrescrever seu comportamento para implementar testes unitários utilizando a técnica Mock Objects, indico a ferramenta PowerMock.

Esse framework permite a criação de mocks de métodos marcados com static ou até mesmo final. O PowerMock é utilizado como uma extensão de outras ferramentas de mock, como o EasyMock e o Mockito.

Exemplo

O trecho de código abaixo retirado deste exemplo, demonstra como utilizar o PowerMock com o EasyMock:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import samples.singleton.StaticService;

import static org.easymock.EasyMock.expect;
import static org.junit.Assert.*;
import static org.powermock.api.easymock.PowerMock.*;

@RunWith(PowerMockRunner.class)
@PrepareForTest( { StaticService.class })
public class MockStaticTest {

 @Test
 public void testMockStatic() throws Exception {
  mockStatic(StaticService.class);
  String expected = "Hello altered World";
  expect(StaticService.say("hello")).andReturn("Hello altered World");
  replay(StaticService.class);

  String actual = StaticService.say("hello");

  verify(StaticService.class);
  assertEquals("Expected and actual did not match", expected, actual);
 }
}

O método estático a ser “mockado” é o say da classe StaticService. Suponha que a implementação deste método retorna “Hello World”. O objetivo deste código de exemplo é sobrescrever o comportamento de say para retornar “Hello altered World” quando recebe “hello” como parâmetro.

A linha 12 define o runner do PowerMock para o JUnit 4 e a linha 13 informa quais as classes que devem ser preparadas para o teste. Entre as linhas 18 e 21 é definido o comportamento esperado para o metodo say, que é retornar “Hello altered World” quando receber “hello” como parâmetro. O método say é chamado na linha 23 e retornará o valor definido, o que é verificado nas linhas 25 e 26.

A utilização do PowerMock é praticamente igual à do EasyMock padrão, com excessão do runner, da annotation @PrepareForTest e da chamada do método mockStatic.

Por que eu preciso disso?

Um caso em que o PowerMock ajuda muito é quando foi utilizado o design pattern Singleton ou um Factory Method estático. Por exemplo, no código a seguir é chamado o método getInstance de ServicoAutenticacao.

public class LoginController {

 public void realizarLogin(String login, String senha) throws LoginInvalidoException {
  ServicoAutenticacao servicoAutenticacao = ServicoAutenticacao.getInstance();

  //faz algo...
 }
}

O problema é que para realizar os testes unitários você não deve utilizar a implementação real deste serviço, já que ele pode depender de recursos (banco de dados, LDAP, etc.) e é necessário sobrescrever seu comportamento para os testes. Com o PowerMock você sobrescreve o método estático para retornar uma implementação fake do serviço ou um mock object.

O ideal neste caso seria substituir o método estático por uma Injeção de Dependência, porém isso não é possível se você estiver utilizando uma API ou framework que tenha uma chamada desse tipo.

Instalação

A página Getting Started da wiki do projeto explica como adicionar a biblioteca em seu projeto, utilizando o Maven ou não.

Referências