Mostrando postagens com marcador mock objects. Mostrar todas as postagens
Mostrando postagens com marcador mock objects. Mostrar todas as postagens

segunda-feira, 17 de março de 2008

Devo mockar métodos de classes "utilitárias"?

Se considerarmos que esses métodos estão fora do escopo testado ou que serão testados isoladamente, a resposta é não, porém, pensando com cuidado, percebo que esses métodos normalmente são recheados de Reflections e Generics para serem o mais genérico possível, e pensando em algumas situações acho que podemos mudar de opinião.

Temos aqui na empresa uma classe CollectionUtil onde criamos métodos para manipular Collections. Nesta classe criamos um método não tão genérico quanto o possível, porém, o suficiente para a nossa utilização, sendo que me deparei com uma coisa:

O método é responsável por receber uma Collection e retornar um Array de ids (todas as nossas entidades tem esse atributo):

 0: public Long[] convertCollectionToArrayIds(
1: Collection coll)
2: {
3: Method mKey = null;
4: Long[] ids = new Long[coll.size()];
5:
6: try
7: {
8: int count = 0;
9: for(T clazz: coll)
10: {
11: mKey = clazz.getClass().
12: getMethod("getId");
13: ids[count] = (Long) mKey.invoke(
14: clazz,new Object[]{});
15: count++;
16: }
17:
18: return ids;
19: }
20: catch(Exception e)
21: {
22: e.printStackTrace();
23: return null;
24: }
25: }

Imagine uma situação onde alguém, não importa o porque, não use o atributo "id" e sim "cod" ou um erro de digitação e sai um "ide", ou qualquer outra coisa, pensando nisso, vejo que a melhor saída é: Não mockar o método "utilitário".

Use o bom senso, um método utilitário que envie um email ou que não seja tão genérico assim, pode ser mockado. Outra alternativa é enriquecer o método "utilitário", fazendo verificações se a entidade possui o atributo "id".

quinta-feira, 6 de março de 2008

Método mockado retornando uma Exception

Recentemente precisei fazer um teste em um método e que deveria verificar o comportamento do mesmo quando um método invocado por ele retornasse uma Exception específica.

Exemplo do método:
0: public String delete() throws Exception
1: {
2: try
3: {
4: vendaManager.delete(venda.getId());
5: }
6: catch (UnexpectedRollbackException e)
7: {
8: addActionError("Não foi possível cancelar venda.");
9: return Action.INPUT;
10: }
11: return Action.SUCCESS;
12: }
No expect do método é só retornar a exception da seguinte forma:
0: vendaManager.expects(once()).method("delete")
1: .with(ANYTHING).will(throwException(
2: new UnexpectedRollbackException("")));
3: assertEquals(action.delete(), "input");

quarta-feira, 16 de janeiro de 2008

Passagem de vários parâmetros para método mockado

De acordo com o post Passagem de parâmetros para método mockado, a quantidade de parâmetros passados para o método mockado deve ser igual a do método "de verdade", desta forma encontrei um problema esta manhã:

Tinha um teste para um método, e nele, mockava um método de um outro objeto da seguinte forma:

historicoClienteDao.expects(once()).method("findByFiltros").with(1L, "nome", null, 3L).will(returnValue(historicos));

porém, este método ganhou mais um parâmetro e quando o adicionei, deu erro de compilação:

historicoClienteDao.expects(once()).method("findByFiltros").with(1L, "nome", null, 3L, true).will(returnValue(historicos));

pois o método with só recebe 4 Constraints.

Aí foi que percebi que ele também recebe um array de Constraints e então o "problema" foi resolvido da seguinte forma:

historicoClienteDao.expects(once()).method("findByFiltros").with(new Constraint[]{1L, "nome", null, 3L, true}).will(returnValue(historicos));


ps. a ferramenta usada neste exemplo é o JMock.

sexta-feira, 14 de dezembro de 2007

JMockit - Mockando um Método Estático

É com grande felicidade que consegui, nesta manhã de sexta, mockar um método estático, p&#@ que p@$%&, sensacional.

O nome do cara é JMockit

Bem, o meu exemplo é bem simples:

Criei uma classe que tem somente um método estático, é esta classe que quero mockar:
0: public class ClasseUtil {
1: public static int metodoEstatico()
2: {
3: return 1;
4: }
5: }
Então, uma classe que utiliza este método:
0: public class ClasseFuncionalidade {
1: public static int metodoLocal()
2: {
3: return ClasseUtil.metodoEstatico();
4: }
5: }
Por fim, a classe de teste:
 0: public class ClasseFuncionalidadeTeste{
1:
2: public static class MockClasseUtil{
3: public static int metodoEstatico()
4: {
5: return 0;
6: }
7: }
8:
9: @Before
10: public void prepare(){
11: Mockit.redefineMethods(
12: ClasseUtil.class,
13: MockClasseUtil.class);
14: }
15:
16: @After
17: public void finalize(){
18: Mockit.restoreAllOriginalDefinitions();
19: }
20:
21: @Test
22: public void metodoLocal(){
23: assertEquals(0, ClasseFuncionalidade.
24: metodoLocal());
25: }
26: }
Perceba que criei uma outra classe (inner class) chamada MockClasseUtil, com um método (metodoEstatico) de mesma assinatura do método da classe ClasseUtil, porém o retorno é diferente (No original é 1 e no falso é 0).

Redefino os métodos da classe real pelos da classe mock:
0: @Before
1: public void prepare(){
2: Mockit.redefineMethods(
3: ClasseUtil.class,
4: MockClasseUtil.class);
5: }
Se fosse no JUnit3, poderia usar o método setUp para invocar o redefineMethods.

E para voltar "ao normal":
0: @After
1: public void finalize(){
2: Mockit.restoreAllOriginalDefinitions();
3: }
A única dificuldade foi "ver" que o comando "-javaagent:jmockit.jar" deve ser passado como parâmetro para a JVM:

No eclipse é só fazer isso, na hora de rodar o teste:



ou isso, para rodar com test suite ou qualquer outro lugar:

quinta-feira, 6 de dezembro de 2007

JMock versus Classes Concretas 2 - A Missão

Me recuperando da decepção da incompatibilidade de versões do JMock, ví que o JMock 1 pode mockar classes concretas.

Baixei o jmock-cglib-version.jar, e fiz como diz no link abaixo:

http://www.jmock.org/jmock1-cglib.html

e funcionou.

A única limitação é que não pode ser uma classe final.



JMock acaba de recuperar seus pontos.


Thanks, Nat Pryce.

quarta-feira, 5 de dezembro de 2007

JMock versus Classes Concretas

O JMock (acho que o EasyMock também) precisa de uma interface para ser instanciado, mas eu precisei usar com uma Classe Concreta.

Sei que a primeira idéia é: "Porque você não cria uma interface?"

No meu caso, até poderia, mas imagine um sistema legado, onde você esteja mantendo, criar uma interface e alterar um monte de classes talvez não seja o ideal.

E isso me motivou a procurar uma solução para este problema, no site do JMock, encontrei uma referencia ao CGLib (links abaixo):

http://www.jmock.org/jmock1-cglib.html
http://www.jmock.org/mocking-classes.html


algumas horas depois...

Nas soluções acima, me parece que só funciona com o JMock 2, e aí vem a notícia mais triste que tive desde que comecei a fazer testes:

Os testes feitos com JMock 1 não rodam se eu mudar o jar do JMock para a versão 2.

Indo no site (http://www.jmock.org/versioning.html) vi isso:


2.0.0 Incompatible API changes, removes API elements deprecated by version 1.2.0.


Bem, pra quem usa JMock 1 e não quer re-fazer TODOS os seus testes, crie uma interface para a classe que será mockada.

O JMock acaba de perder 5 pontos.

terça-feira, 4 de dezembro de 2007

Passagem de parâmetros para método mockado

Bem,

quando você mocka uma classe, você deve informar, no método de teste, o que o método deve fazer, então, vejamos:

classeDao.expects(once()).method("getAlgumaCoisa").
with(eq(1), eq(true)).will(returnValue(obj));

Traduzindo:

A classeDao é um mock, e neste escopo (método de teste), ele será invocado 1 única vez (expects(once())), o método que será chamado é o getAlgumaCoisa (method("getAlgumaCoisa")), quando passar os parâmetros 1 e true (with(eq(1), eq(true))), retorne um Object obj (will(returnValue(obj))).

Simples, porém me deparei com uma situação bem estranha:

O método que eu estava testando, passava como parâmetro a data atual, e não o recebia como parâmetro. Bem, o problema é o fato do método instanciar um objeto e passar como parâmetro para outro método (que iremos mockar).

Vou simplificar com um exemplo:

Na classe classeManager

o metodo fazAlgo():

//faz um blablabla
classeDao.getAlgumaCoisa(new String[]{"1", "2"});
//faz o resto do blablabla

no meu test:

Estou testando a classe classeManager

o método testFazAlgo():

se eu fizer:

classeDao.expects(once()).method("getAlgumaCoisa").
with(eq(new String[]{"1", "2"})).will(returnValue(obj));

não vai funcionar, ele vai me dizer que o método nunca foi invocado.

Pra quem já estudou um pouco de O.O. já deve ter percebido, o objeto que instanciei na classe classeManager (classeDao.getAlgumaCoisa(new String[]{"1", "2"});) é diferente do instanciado na classe de testes (classeDao.expects(once()).method("getAlgumaCoisa").
with(eq(new String[]{"1", "2"})).will(returnValue(obj));).

Então a solução é:

classeDao.expects(once()).method("getAlgumaCoisa").
with(ANYTHING).will(returnValue(obj));

o que quer dizer:

Quando eu passar "Qualquer Coisa" (with(ANYTHING)) para o método getAlgumaCoisa, retorne o obj.

Importante lembrar que a quantidade de parâmetros deve ser igual a do método "de verdade".

segunda-feira, 3 de dezembro de 2007

Quero usar mocks, uso JMock ou EasyMock?

Bem, já vi defensores das duas "ferramentas", eu prefiro o JMock, acho ele muito simples e bem funcional, de qualquer forma, vou colocar aqui os links para download dos dois:

JMock: http://www.jmock.org/
EasyMock: http://www.easymock.org/

Se você quer começar com JMock: http://www.jmock.org/getting-started.html

Pra ver um caso simples das duas formas, isso pode ajudar a escolher: http://blogs.warwick.ac.uk/colinyates/entry/jmock_versus_easymock/

Aqui tem uma tabelinha com algumas diferenças: http://www.tcay.com/dev/JMockVsEasyMock.htm

O que devo saber pra começar a fazer testes?

Inicialmente, aconselho estudar JUnit, que é uma ferramenta excelente para fazer testes unitários em Java e se integra com o Eclipse, Netbeans e qualquer outra IDE que mereça respeito.

Como os casos de uso evoluem e a complexidade dos testes acompanha, aconselho estudar Mock Objects.

Mocks são simulações de objetos, fazendo com que seus testes fiquem presos a um determinado escopo, por exemplo, se você esta fazendo um teste em uma classe de negócio e o método desta classe invoca um método de outra classe que faz uma consulta no banco, você deve usar um Mock para simular o objeto desta outra classe, simulando assim esta consulta, pois a consulta será testada no momento em que o teste para o seu método for criado.

Com isso você pode começar a fazer testes de seus métodos.

No site da ImproveIT existem vários materiais para estudo no assunto, vou linkar aqui em baixo dois textos bem legais:

Um pouco de TDD, é apresentado um exemplo com JUnit: http://www.improveit.com.br/xp/praticas/tdd

Mock Objects, você pode dar uma lida e ter uma boa idéia sobre o assunto: http://www.improveit.com.br/xp/praticas/tdd/mock_objects