Série de trabalhos: Mock Objects

May 2nd, 2009 by rafaelliu Leave a reply »

Estou fazendo dois trabalhos atualmente: sobre Mock Objects (como preparatório para Service Simulation, se tudo der certo) e sobre WSS. Por isso, e também para receber alguns inputs (que seriam muito bem vindos) vou postar algumas coisas da minha pesquisa aqui no blog. E nesse post explico o que são Mock Objects.

Mock Objects são objetos que simulam outros objetos. São principalmente usados em testes de unidade. Existem vários frameworks de Mock Objects mas pessoalmente prefiro o jMock. Abaixo mostro um exemplo de um teste de unidade usando-se TestNG 5.9 e jMock 2.5:

public class PublisherTest {
	private Mockery context = new Mockery();
 
	public PublisherTest() {
		context = new Mockery();
		// para podermos mockar uma classe
		context.setImposteriser(ClassImposteriser.INSTANCE);
	}
 
	@Test
	public void testOneSubscriberReceivesAMessage() {
		// configura mock
		Subscriber subscriber = context.mock(Subscriber.class);
		Expectations expect = new Expectations();
		String message = "message";
 
		// configura estado do objeto
		Publisher publisher = new Publisher();
		publisher.add(subscriber);
 
		// expectativas
		expect.never(subscriber).receive(message);
 
		context.checking(expect);
 
		// executa
		publisher.publish(message);
 
		// verifica
		context.assertIsSatisfied();
	}
 
}

Usamos mocks em cenários onde precisamos testar um objeto que depende de outros. Qual o problema de dependências? O objetivo dos testes de unidade é testar objetos em isolado, mas se usássemos um Subscriber real erros nele poderiam fazer falhar o teste do Publisher (ver figura abaixo). Por isso usamos mocks.

Teste usando-se objetos reais

Teste usando-se objetos reais

Teste usando-se mocks

Teste usando-se mocks

Com mocks podemos não só substituir algum objeto, mas também controlar todas as interações que são feitas com ele: verificar chamadas de métodos, valores de parâmetros, definir valores de retorno, etc. No código acima estamos por exemplo esperando uma chamada ao método receive do subscriber.

Dito o objetivo que motivou a criação dessa técnica, existem outros que vieram a ser supridos por mocks:

  • Abstrair camadas mais baixas do sistema, tanto por elas não estarem prontas quanto por querermos trabalhar offline.
  • Centralizar a configuração de estado no próprio mock ao invés de espalhá-las pelos testes de unidade.
  • Simular condições difíceis de reproduzir, como o lançamento de uma excessão mais esdrúxula ou um valor retorno raro.
  • Verificar mais rapidamente quando um erro ocorre (fail fast), indicando em que passo do comportamento esperado foi errado ao invés de apenas dizer que o estado final do objeto não condiz com o esperado.

Algo muito interessante vindo do uso de mocks é o Need-Driven Development (NDD), mas não pretendo falar dele aqui. Quando sair meu trabalho disponibilizo no blog, mas quem tiver curiosidade pode me escrever, é sempre bom trocar idéias :)

Advertisement

6 comments

  1. dani says:

    eu adoro quando venho aqui. fico vendo um monte de coisa que nao entendo nada. poxa, não entendo nada de computadores, e sei la o que. :(

  2. JEFFMOR says:

    Olá Rafael, muito bom o seu post explicando a necessidade dos Mock’s.
    Você sabe se existe alguma maneira de utilizar os @TransactionAttribute nos testes unitários? Não posso na minha implementação colocar um begin e comit manual (em.getTransaction().commit()). Coloquei um begin e commit no teste unitário envolvendo o método que precise de um commit, foi a única solução que encontrei.
    Queria apenas utilizar as anotações e q fosse simulado pelo contaíner ou por um mock container, vi que o EB3Unit não provê nda disso, jmock tb não encontrei nada. Você sabe de algo nesse sentido?

    • rafaelliu says:

      @JEFFMOR

      Cara, estou escrevendo justamente sobre isso: se pretendes fazer testes de unidade mesmo deverias mockar a UserTransaction. No caso, usar o @TransactionAttribute até te pouparia trabalho pois não havia nada a ser feito.

      No caso de realmente quereres fazer um teste funcional, frameworks mocks não vão criar um ambiente EJB para ti, e realmente, a solução que arranjasse é única. Só te daria a dica de usar AOP, ao invés de envolver todos os métodos com um commit(). O AspectJ agora tá permitindo definição de pointcuts através de anotações, ai tu pode fazer algo do tipo:

      call(@TransactionAttribute * projeto.testes..*.*(..));

      e definir um around() para controle transacional. Desse modo poderias chamar teus métodos transacionais transparentemente.

  3. Flávio Alves says:

    Muito interessante o post, Rafael. Parabéns! Meus testes unitários ainda não passaram do JUnit :P

    Rola de mockar um resultado de uma requisição a um web service?

    Pow, já ouvi falar em TDD, BDD, DDD, agora NDD??? Que parada é essa?

    []s

  4. rafaelliu says:

    @Flávio Alves

    Dá sim, uma das aplicações de mocks é justamente prover um “fake” de um componente ainda não implementado. Mas ai o web service deve estar sendo acessado através de uma interface (ou até um classe concreta, o jMock pode usar manipulação bytecode).

    Vou dedicar um post para escrever sobre estratégias de uso de mocks.

    Sobre NDD, é uma variação do TDD usada pela corrente mockist para desenvolvimento top-down :p

  5. Daniel Pedroso says:

    Liu… experimente o http://mockito.org/ … é show. É o mais fácil de usar que eu achei.

Leave a Reply