sexta-feira, 27 de novembro de 2009

Guicifique seus Testes - Parte 3

Este artigo é terceira parte da tradução para português do interessante artigo Guicing Up Your Testing, June 21, 2007, por Dick Wall. Trata-se de uma leitura interessante, que junta dois tópicos que estou estudando atualmente: Teste Unitário e Google Guice, além de falar um pouco sobre Injeção de Dependências e os Padrões de Projeto Singleton e Factory.

Índice

Introdução ao Guice

Neste contexto é onde o Guice se encaixa perfeitamente. Ele nos dá o poder de definir uma configuração que mapeia as implementações de objetos a uma forma simples de localizá-los e obtê-los. Quando se pede um objeto para o Guice, ele não só irá procurar por aquele que foi solicitado, mas também irá percorrer o objeto procurando pelas suas dependências, e injetando os tipos corretos de dependência para o contexto aplicável. Ou seja, ele fará todo o trabalho de auto-conectar as dependências entre os objetos, eliminando a necessidade de injetá-las manualmente usando passagem de parâmetros pela aplicação.

Vejamos como é fácil começar a usar o Guice: baixe a versão mais atual desta biblioteca em http://code.google.com/p/google-guice, descompacte o arquivo, procure pelo arquivo guice-VERSION.jar, e adicione-o ao classpath de seu projeto. Feito isso, o Guice está pronto para usar. Ele nada mais é do que um conjunto de anotações para classes e uma simples API para configurar os módulos que irão conectar os objetos da maneira adequada. NA verdade ele é um pouco mais que isso, mas neste momento focarei apenas nas funcionalidades principais.

Voltando ao Exemplo

Vejamos o que o Guice pode fazer por nós. Primeiro, vamos deixar para o Guice a responsabilidade de preparar a classe Pedido toda vez que precisarmos utilizá-la. Sendo mais específico, queremos que o Guice forneça o GerenciadorTaxasJuros adequado para o contexto que a classe Pedido está sendo usada, seja em testes ou em produção:

As mudanças neste ponto incluem:

  • importar a anotação Inject; e
  • adicionar a anotação @Inject antes do construtor da classe Pedido.

Pode-se notar também que fizemos algumas regressões na modelagem, pois já não mais passamos um objeto Cliente através do Construtor - ao invés disso, agora ele deve ser injetado via um método setter. Desta forma, também é necessário retirar do atributo cliente o modificador final, pois para poder usar o setter, o atributo precisa ser mutável. Demos alguns passos para trás, mas voltaremos a falar sobre isso no final deste artigo.

Por agora, a anotação @Inject é a coisa mais importante a se notar. Ela indica para o Guice que ele é responsável pela construção de objetos dessa classe. Por mais que também seja possível criar objetos utilizando a classe da forma tradicional, iremos a partir de agora usar o Guice para fazer isso por nós, pois ele:

  1. Verifica que há uma anotação @Inject no nível do construtor.
  2. Verifica a assinatura dos tipos de parâmetros do construtor.
  3. Para cada um dos parâmetros, ele tenta encontrar um objeto adequado e injetar no objeto, durante a sua criação.
  4. Não há passo 4 (bom, na verdade há: se por algum motivo o Guice tiver problemas em resolver as dependências concretas que deve passar para algum parãmetro do construtor, ele automaticamente lança um erro que nos ajudará a rastrear e corrigir o problema).

Tudo isso parece mágica, mas não é. Para o Guice funcionar, informaremos a ele tudo o que ele precisa saber sobre como criar os objetos que irá injetar em cada parâmetro do construtor. Vamos juntar as peças.

Configuração dos Bindings (conexões entre objetos)

Primeramente, explique para o Guice o que você pretende fazer quando ele for solicitado para injetar um GerenciadorTaxasJuros em outro objeto (lembre que GerenciadorTaxasJuros é uma interface, então é necessário criar alguma de suas implementações concretas para que possa ser utilizado).

Para ensinar o Guice, criamos um módulo de configuração, com um binding dentro dele:

Pronto! Isto cria uma subclasse de Module explicando ao Guice que, quando você precisar de um GerenciadorTaxasJuros, o que você realmente quer, para propósitos de teste, é um objeto de GerenciadorTaxasJurosFake. Em produção, você pode ter um outro módulo que instrui o Guice a usar a implementação GerenciadorTaxasJurosBd.

É aqui que o "pulo do gato" de auto-conexão entre os objetos (auto-wiring) ocorre. Logicamente, em um sistema real, provavelmente haverão muito mais definições de binding, onde tudo o que é necessário é simplesmente adicionar mais chamadas binder.bind. Muitos erros encontrados em grandes sistemas que usam o Guice podem ocorrer devido à ausência de bindings, mas estes tipos de erro são reportados de uma maneira que se torna óbvio percebr quando algo está faltando.

Tem mais uma coisa: mencionamos antes que usar um único objeto GerenciadorTaxasJuros em toda a JVM seria uma boa idéia, porém fazer isso na forma de singleton estáticos é algo muito limitador (o alto acoplamento dificulta, entre outras coisas, escrevermos bons testes). Guice vem nos salvar deste embróglio provendo um "escopo" Singleton para podemos implementar os bindings. Uma forma de se fazer isso, é adicionar na configuração uma chamada ao método in(Scopes), onde Scopes é uma classe da API do Guice:

A outra forma, ainda mais prática, é utilizar a anotação @Singleton diretamente na definição da classe GerenciadorTaxasJurosFake.

Ambos possuem vantagens e desvantagens, porém é muito mais uma questão de preferência qual delas se deve escolher. Prefiro usar a anotação, pois ela expressa diretamente na classe GerenciadorTaxasJurosFake a intenção semântica de que um único objeto dela será criado para atender a todos as dependências a esta classe dentro da JVM.

Nenhum comentário:

Postar um comentário