Tempo de leitura: menos de 1 minuto
E aí pessoal, tudo bem por aí?
Hoje vou dar início uma nova série, chamada “Caixa de ferramentas“. Nesta série, pretendo falar um pouco de bibliotecas Java ou Groovy que ajudam muito durante o desenvolvimento . São bibliotecas que não devem faltar.
Para começar, falaremos hoje de um utilitário chamado Whitebox, ele disponibiliza vários métodos utilitários para acesso interno de classes, incluindo acesso a atributos e métodos privados. Basicamente, um utilitário de reflexão simplificados destinados a testes.
Whitebox faz parte da biblioteca Power Mock. Mock são muito usados em teste, eles simulam comportamentos de objetos, facilitando assim o teste unitário de um determinado comportamento. Mas vamos deixar os mocks para outra ocasião.
Existem situações onde precisamos testar um determinado ação de uma classe, mas a mesma não pode ser testada unitariamente, pois o método é privado. Em alguns casos, estes métodos são muito importantes na classe, mas apenas conseguimos testa-los de forma integrada, executando o método público que executa essa ação.
nestas situações, fica muito difícil criar vários cenários de teste para cobrir o comportamento de uma ação, facilitando a existência de bugs.
Um exemplo prático dessa situação ocorreu no meu último post (Reflection e Annotation). A classe MascaraUtil, possui apenas um método público, o aplicarMascara, este foi o único método que eu testei de verdade, pois era o único que permitia o acesso. No entanto, outros métodos da classe com getValorComMascara e removerMascara, deveriam ser testados de forma unitária.
É aí que o Whitebox, pode nos ajudar e muito. Vamos usar a classe MascaraUtil como exemplo, criando testes unitário para os métodos privados.
[code language=”java”]
@Test
public void testRemoverMascara() throws Exception {
final MascaraUtil mascaraUtil = new MascaraUtil();
// invocação método privado
// Cenário 1
Object retornoMetodoPrivadoRemoverMascara = Whitebox
.invokeMethod(mascaraUtil, "removerMascara", "01…234***56.789-0");
System.out.println(retornoMetodoPrivadoRemoverMascara);
Assert.assertEquals(CPF_PADRAO, retornoMetodoPrivadoRemoverMascara);
// Cenário 2
retornoMetodoPrivadoRemoverMascara = Whitebox
.invokeMethod(mascaraUtil, "removerMascara", "abc(def)….—>>123");
System.out.println(retornoMetodoPrivadoRemoverMascara);
Assert.assertEquals("abcdef123", retornoMetodoPrivadoRemoverMascara);
}
[/code]
No teste testRemoverMascara usamos o método invokeMethod, passando o objeto mascaraUtil, o nome do método privado e o parâmetro solicitado pelo método. O invokeMethod sempre retorna um Object, no caso a String, sem caracteres especiais. Simples não? Se quiser criar vários cenários de teste para validar esse método fica muito mais simples.
[code language=”java”]
@Test
public void testGetValorComMascara() throws Exception {
final MascaraUtil mascaraUtil = new MascaraUtil();
// Com Whitebox, também é possível acessar os fields de uma classe e obter ser dados
final Field cpf = Whitebox.getField(Pessoa.class, "cpf");
final String mascaraCpf = cpf.getAnnotation(Mascara.class).value();
System.out.println(mascaraCpf);
// invocação método privado
// Tenha muita atenção na ordem dos parametros pois isso pode fará que o resultado dê errado
// Cenário 1
Object retornoMetodoPrivadoRemoverMascara = Whitebox
.invokeMethod(mascaraUtil, "getValorComMascara", mascaraCpf, "01…23456.789-0");
System.out.println(retornoMetodoPrivadoRemoverMascara);
Assert.assertEquals(CPF_MASCARADO_PADRAO, retornoMetodoPrivadoRemoverMascara);
// Cenário 2
retornoMetodoPrivadoRemoverMascara = Whitebox
.invokeMethod(mascaraUtil, "getValorComMascara", "AA#.##", "ab.123");
System.out.println(retornoMetodoPrivadoRemoverMascara);
Assert.assertEquals("ab1.23", retornoMetodoPrivadoRemoverMascara);
}
[/code]
O mesmo ocorre no teste do método getValorComMascara, usando o mesmo método invokeMethod, podemos criar vários cenário de teste para valida-lo. Note que na linha 6, usei o método getField para conseguir obter a máscara do atributo cpf.
Criei também um exemplo, usando o método invokeConstructor, usado para instância um objeto, e setInternalState, para setar valores em atributos privados, sem métodos de acesso. Para este exemplo. usei a classe Pessoa, também usando do post (Reflection e Annotation), com algumas modificações:
[code language=”java”]
public class Pessoa {
@Mascara("###.###.###-##")
private String cpf;
private String nome;
public Pessoa() {
nome = "Eder";
}
public String getCpf() {
return cpf;
}
public String getNome() {
return nome;
}
public void setCpf(final String pCpf) {
cpf = pCpf;
}
}
[/code]
O teste ficou assim:
[code language=”java”]
@Test
public void testeSetNome() throws Exception {
// contruindo objeto via Whitebox
final Pessoa pessoa = Whitebox.invokeConstructor(Pessoa.class);
System.out.println(pessoa);
Assert.assertEquals("Eder", pessoa.getNome());
// alterando o nome da pessoa (atributo privado), usando setInternalState
Whitebox.setInternalState(pessoa, "nome", "Eder Leite");
System.out.println(pessoa);
Assert.assertEquals("Eder Leite", pessoa.getNome());
}
[/code]
O Whitebox possui outros métodos úteis, como getAllStaticFields e getFieldsAnnotatedWith, para saber mais, acesse o Java Doc.
O código fonte com os exemplos, está disponível no Github.
O Power Mock, também está disponível no Github.
Se gostou, compartilhem com seus amigos!
Abraços.