Mockando construtores

Tempo de leitura: menos de 1 minuto

Fala galera.

Semana passada está criando um teste unitário para um mecanismo (serviço), e me deparei com um problema, dada a forma como o serviço funcionava não conseguia definir fortemente minha massa de dados e o retorno esperado.

Este mecanismo tem a responsabilidade de atualizar a o valor da multa de débito, ou seja, se o débito está vencido, este cara calcula o valor da multa para o mês atual. Sendo assim o valor esperado muda todos os meses, não sendo possível fazer o assert de forma estática.

Então pensei em mocar (mock) o mês atual, isto me garantiria que o mês esperado seria sempre o mês que eu definisse, fazendo com que o valor esperado seja sempre o mesmo. Ok, isto resolveria meu problema com certeza, isto se o mecanismo não usasse um utilitário chamado Periodo.

Periodo é um classe utilitário que resolver, situação relacionadas a um período (mês/ano). Ele tem um construtor default que usa a data atual para inicializar o período, então se desse um new Periodo() hoje (06/04/2017), Periodo seria iniciado com data início 01/04/2017 e data fim 30/04/2017.

Então no meu caso, teria que mocar o construtor default do Periodo, assim ele sempre criaria um período com o mês que eu esperava no teste. Mau sabia eu, isto ia dar um puta trabalhão.

Primeira tentativa

No projeto já tínhamos o PowerMock para esta necessidade, mas esta biblioteca é um pé saco, para mocar o construtor, eu também teria que mocar todos os métodos que o mecanismo usado, isso era inviável já que o período possui dezenas de métodos, vários eram utilizados pelo mecanismo.

O que eu realmente precisava era “substituir” a instância de período que o construtor default cria, por um pré construído pelo teste, assim todos os métodos funcionaram normalmente, sem mais intervenções.

Segunda tentativa

Como ficou complicado, converti meu teste para Groovy (outra biblioteca que temos no projeto). O Groovy tem um utilitário muito legal para trabalhar com mocks, o MockFor.

O exemplo que via na net foi o seguinte:

[code language=”java”]
import groovy.mock.interceptor.MockFor

class Person {
String name
}

def tom = new Person(name:’Tom’)
def mock = new MockFor(Person, true)

mock.demand.with {
Person() { tom }
}

mock.use {
def mary = new Person(name:’Mary’)
assert tom == mary
}
[/code]

Neste código, é instanciado uma Person cujo o name foi setado para Tom (linha 7). No construtor do MockFor, além de passar a classe que será mocada, passou também true, isto para informar que o construtor da classe será mocado (linha 8).

Em seguinte, informou que o construtor default de Person retornaria a variável “tom” quando fosse chamada (linhas de 10 à 12) . Por fim usou o mock nas linha 14 à 17.

Vi isto e falei “pronto, resolvi meu problema”. Rsrs… Não foi bem assim, este tipo de mock funciona apenas com classe Groovy, com Periodo é uma classe Java, tomei uma exceção na cara.

Terceira e última tentativa

Procurei na net um forma que fazer isto funcionar, e encontrei outra biblioteca PowerMockito, uma mistura de PowerMock e Mockito. Não é resolveu meu problema?

 

Encontrei este exemplo (MockConstructionTest.java) e adicionei a seguinte linha de código ao meu teste:

[code language=”java”][/code]

PowerMockito.whenNew(Periodo).withNoArguments().thenReturn(new Periodo(“05/2016″))

[code][/code]

Não é só isso resolver meu problema!!

Ah… Precisei também anotar o teste com:

[code language=”java”][/code]

@RunWith(PowerMockRunner.class)
@PrepareForTest(MecanismoCalculoServiceImpl.class)
class ControleSaldoNegativoServiceImplMockTest {
   //...
}

[code][/code]

Gostei bastante de usar este PowerMockito, ficou bem simples e não é verboso como outras soluções que testei.

Grande abraço pessoal.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *