• Nenhum resultado encontrado

Convivendo e melhorando seu relacionamento com código legado utilizando PowerMock

N/A
N/A
Protected

Academic year: 2021

Share "Convivendo e melhorando seu relacionamento com código legado utilizando PowerMock"

Copied!
9
0
0

Texto

(1)

: : www.mundoj.com.br : :

Convivendo e melhorando seu

relacionamento com código legado

utilizando

PowerMock

Marcio Garcia “Mangar”

(marcio.mangar@gmail.com twitter: @mangar) bacharel em Ciência da Computação. Trabalha com Java desde 1996. Já trabalhou em empresas de pequeno e grande porte bem como startups no Brasil e Austrália desenvolvendo, migrando e integrando sistemas em Java e Ruby on Rails. Possui certificações SCJP, SCWCD, SCBCD, SCEA I e CSM. Atualmente é consultor independente, e mantém seus dois blogs: http://blog. mangar.com.br e http://marciogarcia.com.

á 15 anos surgia uma das mais populares e revolucio-nárias linguagens de programação. Por todo este tempo muitas aplicações foram construídas utilizando as mais diversas técnicas e bibliotecas. TDD surgiu juntamente com XP em meados de 1999. Ou seja, primeiro nos preocupamos em desenvolver, depois em desenvolver corretamente. Nada mais natural. No entanto, 15 anos depois ainda temos que conviver e muitas vezes integrar nossas aplicações com código legado dos anos 90.

Neste artigo apresentaremos soluções de como integrar código le-gado e ainda assim fazer bom uso de técnicas de teste, como TDD. Também mostraremos como utilizar a ferramenta PowerMock que fará praticamente todo o “trabalho sujo” de testar o código legado-intestável e nossos exemplos.

Como consumidores, garantia é algo que todos exigimos. Imagine se fosse possível comprar um automóvel novo, e no contrato ti-WFTTFVNBDMÈVTVMBEJ[FOEPi7FÓDVMPTFNHBSBOUJBw$FSUBNFOUFP

fabricante cairia em descrédito, você não levaria o carro para casa e ainda faria propaganda negativa a respeito do fabricante. Transporte este exemplo para o mundo de desenvolvimento de software. Um empregador em plena consciência contrataria algum fornecedor de serviços em que no contrato constasse: “Software sem garantia”?

Garantia de um produto não está restrito em apenas um aspecto. Mas em se tratando de desenvolvimento de software um dos pila-res para se alcançar um software de qualidade é um código limpo e testado.

Mas para se alcançar tal cenário, com código limpo e testado al-guns percalços devem ser transpostos. O primeiro é a escrita de um código testável. Se um código é escrito sem a preocupação com a implementação de testes, este será um código muito difícil de ser garantido.

Códigos intestáveis são frequentemente encontrados em sistemas Neste artigo, vamos mostrar algumas técnicas para

um convívio pacífico entre as gerações de código e ainda mostrar uma ferramenta utilizada para testar código até então intestável, a fim de proteger as funcionalidades já implementadas e readequar o código legado em um mais moderno e bem desenhado.

(2)

Legibilidade de Código

Código testável é algo que deveria estar na linha de frente do ba-cklog técnico de qualquer projeto de desenvolvimento de softwa-re. E não apenas na forma de percentual de cobertura do código, mas também na forma de legibilidade de código.

Um código ilegível, sem qualquer dúvida, te levará a além de uma equipe desmotivada com performance sofrível também a um código “intestável”. E esta combinação altamente explosiva contri-buirá muito para problemas inesperados no projeto, em especial a não-geração do valor esperado para o cliente.

Pontos de atenção

Algumas práticas conduzem a um código não muito expressivo e ilegível e com isso um código de difícil implementação de testes e algumas vezes até mesmo intestável:

t 6UJMJ[BÎÍPEF4FSWJDF-PDBUPS t 4JOHMFUPOTo7FKBCPY0TNBMFGÓDJPTEP4JOHMFUPO t #MPDPTFTUÈUJDPTOBJOJDJBMJ[BÎÍPEBDMBTTF t *OJDJBMJ[BÎÍPEBDMBTTFOÍPÏDPNQMFUBNFOUFJNQMFNFOUBEPOP construtor. t 7JPMBÎÍPEB-FJEF%FNFUFSo7FKBCPY-FJEF%FNFUFS Estes são alguns exemplos de más práticas que devem ser evitadas no desenvolvimento de qualquer aplicação. Se trechos de código utilizando algumas das práticas citadas existem no seu projeto, a primeira ação é substituí-las por convenções de código mais legíveis.

No entanto, algumas vezes, por motivações diversas, como com-pra de uma determinada biblioteca ou utilização de uma bibliote-ca de código cujo o código-fonte não esteja mais disponível, será necessário o convívio com tais códigos.

Uma boa iniciativa é reduzir a disseminação deste código dentro do seu projeto. A criação de pontos de integração (seams, em inglês) reduz significativamente a dependência de tais códigos.

Mecânica do Refactoring

Lembre-se: estamos lidando com um código legado, no qual po-demos encontrar implementações bizarras, como classes extrema-mente longas, com algo em torno de 200 linhas, com vários níveis de abstração entre os métodos.

Como um bom desenvolvedor você deve deixar a classe melhor – mais legível, testável etc. – do que quando você a pegou. Para conseguirmos tal feito sem que nenhuma funcionalidade deixe de funcionar corretamente, é essencial que antes de fazermos o

Figura 1. Mecânica do refactoring.

em algum ponto realmente crítico do software.

Neste artigo apresentaremos técnicas e ferramentas para aprimo-rar o código legado com segurança, implementando testes unitá-rios neste código antes de fazermos o refactoring para uma versão legível e testável do código legado.

Na figura 1 é mostrada a mecânica a ser seguida no refactoring de um código legado.

PowerMock

PowerMock é um complemento aos frameworks que geram NPDLPCKFDUT o7FKB#PY.PDL0CKFDUToDPNPP&BTZ.PDLF o Mockito. Com ambos a integração é bastante natural. Não há necessidade de nenhum tipo de adaptação para a integração. O framework é distribuído sobre a licença Apache License 2.0. O código-fonte está disponível no Google Code em: http://code. google.com/p/powermock/. Os principais mantenedores são Jan Kronquist e Johan Haleby que trabalham na empresa sueca Jayway.

A motivação para a utilização do framework é resolver problemas relacionados à testabilidade de código legado ou de terceiros. Có-digos difíceis de testar ou mesmo impossíveis passam a ter suas chances utilizando o PowerMock.

7ÈSJPTFYFNQMPTQPEFNTFSFODPOUSBEPTOPTJUFEP1PXFS.PDL utilizando EasyMock e Mockito. Neste artigo, abordaremos a uti-lização integrada entre PowerMock e Mockito.

Funcionamento do framework

O PowerMock é um framework que estende outras bibliotecas, como Mockito e EasyMock, incluindo funcionalidades mais avan-çadas, tais como:

t .PDLEFNÏUPEPTFTUÈUJDPT t .PDLEFNÏUPEPTFDMBTTFTmOBJT t .PDLEFNÏUPEPTQSJWBEPT t .PDLEFDPOTUSVUPSFT t .PDLQBSDJBM

Para tais feitos, o framework faz uso extensivo de manipulação de bytecode através de um classloader próprio. Para a manipulação de bytecode, ele faz uso da biblioteca Javassist (http://www.jboss. org/javassist).

(3)

: : www.mundoj.com.br : :

Listagem 1. Cliente do ServiceLocator: SLClient.java. publicclass SLClient {

privatestatic String JNDI_LOCATION = “not loaded”;

static {

JndiLocationsLoader loader = newJndiLocationsLoader(); SLClient.JNDI_LOCATION = loader.getFirstJndiLocation(); }

publicvoidexecuteARemoteMethod() {

ServicoDeExclusao servico = (ServicoDeExclusao) ServiceLocator .getInstance(SLClient.JNDI_LOCATION).lookup(

ServicoDeExclusao.class);

servico.delete(“some object to be excluded”); }

}

Service Locator e Singleton

Integração com JUnit

Neste artigo utilizaremos JUnit para os testes de unidade juntamente com Mockito e o PowerMock para a criação dos mock objects. A integração com JUnit é muito simples, basta incluir duas anotações em seu código de teste:

t !3VO8JUI t !1SFQBSF'PS5FTU

@RunWith: é uma anotação do JUnit. Esta define qual será o Runner a ser executado pelo JUnit, por padrão, quando não informado ne-nhum é utilizado o próprio Runner da versão do JUnit que você está utilizando (configurada no pom.xml).

Em nossa implementação de testes utilizando o PowerMock será neces-sário redefinir o Runner, para que seja utilizado o runner do Mockito com JUnit, definido pela classe MockitoJUnitRunner.class

@PrepareForTest: é uma anotação que faz parte do PowerMock. Esta informa ao classloader do framework quais serão as classes que possivelmente sofrerão manipulação de bytecode. Como parâmetro, esta anotação recebe uma classe ou um array de classes.

Agora que já estamos acertados quanto ao fluxo que iremos utilizar na readequação do código legado (figura 1) e no ferramental que iremos utilizar na tarefa de implementação dos testes vamos percorrer os pro-blemas que facilmente serão encontrados em código legado.

O primeiro exemplo será a implementação de testes em código utili-zando Service Locator com Singleton e Inicialização Estática de Objetos

Nos tempos em que não se falava em Injeção de Dependência, os Service Locators eram a “grande onda”. Utilizar este Design Pattern era sinal de que sua aplicação fazia uso da mais avançada e excepcional tecnologia em que poderia se ter contato no mundo Java: EJB 2.X.

As mais avançadas implementações deste Pattern sugeririam in-clusive a instanciação única e cache dos recursos adquiridos. Isso quer dizer, o Service Locator era um Singleton e um controlador de Cache, infringia duas vezes o princípio da Responsabilidade ÁOJDB7FKBPCPY1SJODÓQJPEB3FTQPOTBCJMJEBEFÁOJDB

Iremos testar três pontos em um cliente que fará uso do nosso ServiceLocator:

t NPDLEPNÏUPEPFTUÈUJDPOP4FSWJDF-PDBUPSHFU*OTUBODF t UFTUBSPNÏUPEPWPJEEFOPTTPDMJFOUF

Na Listagem 1 você encontrará um trecho do nosso Cliente (SL-Client.java) e na Listagem 2 um trecho do ServiceLocator, nada que possamos nos orgulhar de ter implementado, mas ainda assim um ServiceLocator válido.

Mesmo utilizando um classloader próprio, o framework é pouco intrusivo e de fácil utilização. Se você possui conhecimentos bá-sicos de EasyMock ou Mockito, à primeira vista, terá a impressão de que está utilizando apenas um framework.

Listagem 2. ServiceLocator publicclass ServiceLocator {

private String jndi;

privatestatic ServiceLocator instance;

privateServiceLocator() { }

publicstatic ServiceLocator getInstance(String jndi) { if (ServiceLocator.instance == null) {

ServiceLocator.instance = newServiceLocator(); instance.jndi = jndi;

}

return ServiceLocator.instance; }

public Object lookup(Class<?> className) {

System.out.println(“Locate Class: “ + className.getName() + “ @ “

+ jndi);

returnnull; }

}

Inicializamos nossa classe de teste com as duas anotações já co-mentadas anteriormente: @RunWith e @PrepareForTest. Respecti-vamente informando que vamos utilizar o Runner do PowerMock e a classe a ser manipulada será a ServiceLocator.class. A Listagem 3 mostra esta inicialização. Mais adiante temos as declarações e as inicializações dos mock-objects utilizando a anotação @Mock do próprio Mockito (org.mockito.Mock). Utilizando esta anotação torna-se desnecessário a inicialização dos objetos mock, por exem-plo, dentro do método anotado com @Before

@Before

publicvoidsetUp() {

PowerMockito.mockStatic(ServiceLocator.class); }

Método @Before setUp: neste ponto começam as alterações específicas para o PowerMock.

(4)

Listagem 3. Teste do SLCliente.java. @RunWith(PowerMockRunner.class)

@PrepareForTest(ServiceLocator.class)

publicclass SLClient_Test { @Mock ServicoDeExclusao servicoDeExclusao; @Mock ServiceLocator serviceLocator; @Before

publicvoidsetUp() {

when(serviceLocator.lookup((Class<?>)any())).thenReturn(servicoDeExclusao);

PowerMockito.mockStatic(ServiceLocator.class);

when(ServiceLocator.getInstance(anyString())).thenReturn(serviceLocator); }

@Test

publicvoidexecuteARemoteMethod_Test() { SLClient client = newSLClient(); client.executeARemoteMethod();

verify(servicoDeExclusao).delete(anyObject()); }

}

jeto recém-mocado chamado servicoDeExclusao.

Adiante, utilizando o PowerMock, informamos que a classe Ser-WJDF-PDBUPSEFWFSÈTFSJOUFSDFQUBEBQFMB7./BÞMUJNBMJOIBEP setUp, voltamos a utilizar o Mockito para definir o resultado da chamada ao método getInstance, que é um método estático da classe ServiceLocator.

Um ponto interessante a ser notado é que a variável servicoDeE-xclusao não é uma variável de instância e muito menos injetada na classe cliente. Esta é carregada no escopo do método através de uma chamada a um método estático do ServiceLocator. Para testar o método executeARemoteMethod seria necessário simular todo o comportamento dos métodos: getInstance e lookup da classe Servi-ceLocator. Certamente seria muito trabalhoso.

Outra má prática duplamente e propositalmente implementada neste exemplo é a utilização de métodos void. Tendo como foco a testabilidade, uma implementação como esta é inviável de ser testada sem nenhuma readequação.

Uma funcionalidade já manjada é a utilização dos métodos verify() para este propósito, já que não é possível verificar através de As-serts o retorno dos métodos ao menos conseguiremos verificar se a chamada ao serviço – em nosso caso o método delete() – foi feita.

Desabilitando recursos

Na Listagem 4 trazemos a saída em console da execução da classe teste SLClient_Test.

Execução da Inicialização Estática de SLClient. Constructor de Base Client

Chamada a um WS informando que algum cliente está ativo .. mj.teste. servicelocator.client.SLClient

Construtor de StClient

Chamada a um WS informando que o cliente Cliente está ativo .. mj.teste. servicelocator.client.SLClient

execução de executeDeleteMethod

Listagem 5. Cancelando a execução dos Construtores – SL-Client_MockConstrutor_Test.

@RunWith(PowerMockRunner.class)

@PrepareForTest({ServiceLocator.class, BaseClient.class})

publicclass SLClient_MockConstrutor_Test {

//...

@Before

publicvoidsetUp() {

PowerMock.suppress(PowerMock.constructor(SLClient.class)); //...

} }

Construtores

A classe SLClient é subclasse de BaseClient, que por sua vez ao ser instanciada efetua uma chamada fictícia a um WebService solicitando o registro da mesma. Mais detalhes podem ser vistos no código-fonte completo. Este é apenas um exemplo de tarefas complexas e dependentes de recursos externos como conexão com a web, que podem estar definidos dentro do construtor. Algumas vezes é interessante quando não essencial suprimir algum construtor para que você possa prosseguir com os testes da classe. Para suprimir um construtor com PowerMock basta seguir os passos: 1) inclua a classe na anotação @PrepareForTest e 2) Suprima o construtor. A Listagem 5 mostra como suprimir o construtor padrão da classe SLClient.

Se a classe tiver mais de um construtor você pode definir qual dos construtores poderá ser suprimido apenas informando os tipos dos parâmetros na chamada do método PowerMock.construc-tor(). Este é um exemplo caso a classe SLClient além do constru-tor padrão também tivesse um construconstru-tor com uma String como parâmetro.

PowerMock.suppress(PowerMock.constructor(SLClient.class, String.class));

Métodos

Menos raros, mas ainda comuns, são os casos em que pode ser neces-sário desabilitar a execução de alguns métodos.

7PDÐ UFN EVBT PQÎÜFT EF GB[FS JTTP " QSJNFJSB Ï VUJMJ[BOEP B KÈ conhecida chamada PowerMock.suppress alterando o parâmetro de

(5)

Listagem 6. Cancelando a execução de métodos e utilizando PartialMock para substituir apenas métodos específicos. @RunWith(PowerMockRunner.class)

@PrepareForTest( { ServiceLocator.class, SLClient.class })

publicclass SLClient_Method_Test { //...

@Test

publicvoiddesabilita_metodo_suppress_Test() {

PowerMock.suppress(PowerMock.method(SLClient.class, “getInitializedFromConstructor”));

SLClient client = newSLClient();

String a = client.getInitializedFromConstructor(); assertNull(a);

client.executeDeleteMethod();

verify(servicoDeExclusao).delete(anyObject()); }

@Test

publicvoidpartial_mock_Test() {

SLClient client = PowerMock.createPartialMock(SLClient.class, “getInitializedFromConstructor”);

String a = client.getInitializedFromConstructor(); assertNull(a);

client.executeDeleteMethod();

verify(servicoDeExclusao).delete(anyObject()); }

}

Métodos privados

Chegamos a um ponto muito delicado: os métodos privados. Al-guns dizem que testar métodos privados é desperdício de tempo, uma vez que os métodos públicos que fazem uso dos métodos privados já são testados e com isso os privados já o são. Outros rebatem dizendo que é muito trabalhoso implementar e manter testes de métodos públicos incluindo as verificações necessárias requeridas.

Para este exemplo fizemos uma pequena alteração na classe SL-Client.java. Introduzimos um método privado (isCliente) retor-nando um booleano. A partir deste retorno é que o serviço de exclusão (service.delete()) do cliente é chamado (Listagem 8). Antes de escolher um lado nesta batalha, dê uma olhada no artigo

: : www.mundoj.com.br : :

PowerMock.constructor para PowerMock.method. A segunda é efe-tuando um PartialMock, onde apenas os métodos declarados serão substituídos.

Ambas as implementações obterão o mesmo resultado com a diferen-ça de que utilizando createPartialMock o construtor é substituído, ou seja, ele não é executado. A não ser que seja trocado o método crea-tePartialMock por creacrea-tePartialMockAndInvokeDefaultConstructor. 7PDÐQPEFBDPNQBOIBSPSFTVMUBEPBUSBWÏTEPEFCVHPVEPDPOTPMF Dessa forma fica mais fácil acompanhar o que acontece quando al-gum recurso é substituído, mesmo apenas tendo o console.

em que a equipe de testes do Google escreveu sobre qual o mí-nimo e o máximo a ser testado em: http://googletesting.blogspot. com/2008/02/in-movie-amadeus-austrian-emperor.html.

Se você optou por escolher o primeiro lado, os que acham que testando o método público também estará testando os métodos privados associados a ele, a Listagem 7 é para você.

A implementação segue o mesmo modelo de métodos públicos, adicionando duas chamadas:

t 1PXFS.PDLFYQFDU1SJWBUF t 1PXFS.PDLSFQMBZ

Listagem 7. Substituição de métodos privados. SLClien-te_Method_Test.java.

@Test

publicvoidnotAClient_False_Test() throws Exception {

SLClient client = PowerMock.createPartialMock(SLClient.class, “isCliente”);

boolean expectedResult = false;

PowerMock.expectPrivate(client, “isCliente”, “cliente 1”). andReturn(expectedResult);

PowerMock.replay(client);

client.executeDeleteMethod();

verifyZeroInteractions(servicoDeExclusao); }

Listagem 8. Inclusão do método privado isCliente e utilização do mesmo. SLCliente.java.

publicvoidexecuteDeleteMethod() {

System.out.println(“execução de executeDeleteMethod”); ServicoDeExclusao servico = (ServicoDeExclusao) ServiceLocator. getInstance(SLClient.JNDI_LOCATION_1).lookup(ServicoDeExclusao.class);

String cliente = “cliente 1”; if (isCliente(cliente)) { servico.delete(cliente); } else {

System.out.println(“nothing to do”); }

}

private Boolean isCliente(Object o) { return o != null;

}

Utilizando o PowerMock, você também pode chamar métodos privados de fora das classes. Não tão simples quanto se estivesse utilizando Groovy, mas ainda assim possível. A Listagem 9 mostra como fazer.

Na Listagem 9, todo o conteúdo do método isCliente foi ignorado, e quando chamado retornará o valor true, ou seja, este é um cliente. Foi introduzida uma nova funcionalidade do framework PowerMo-ck, a classe Whitebox, que será descrita em detalhes mais adiante.

(6)

@Test

publicvoidcalling_private_method_Test() throws Exception {

SLClient client = PowerMock.createPartialMock(SLClient.class, “isCliente”);

boolean expectedResult = true;

PowerMock.expectPrivate(client, “isCliente”, “cliente 1”). andReturn(expectedResult);

PowerMock.replay(client);

boolean result = Whitebox.invokeMethod(client, “isCliente”, “cliente 1”);

assertTrue(result); }

Inicializadores estáticos

A motivação para implementação de inicializadores estáticos de classe é a de assegurar que determinado bloco seja executado sempre que esta classe seja inicializada não importando qual construtor utilizado, caso esta tenha mais de um. As motivações para a não utilização variam em torno da quebra de OO e a dificuldade quanto aos testes desta classe. Existe mais de uma alternativa para a não utilização de inicializado-res estáticos, a mais simples é incluir todas as chamadas necessárias à inicialização da classe no construtor da classe. Caso a classe possua mais de um construtor e estes possuam uma inicialização em comum, uma alternativa é criar um método de inicialização comum e também sempre usar encadeamento de construtores, quando possível. Alternativas mais elaboradas passam por criação de um Proxy, utilizan-do InvocationHandler (veja um exemplo da criação de um Proxy nos links de referência deste artigo). Uma solução também poderia ser a utilização do design pattern Builder.

Enfim, alternativas para a não utilização de um bloco estático na inicia-lização de uma classe existem e passam das mais triviais para as mais elaboradas, cabe a você escolher uma. Se estiver na dúvida de qual seria a melhor a ser utilizada, eu sugiro fazer uma prova de conceito de cada alternativa, assim você terá uma real experiência das implementações e do esforço de cada uma diante de seu projeto.

Em nosso exemplo, vamos desabilitar a inicialização estática de uma classe. Utilizaremos um construtor padrão para isso, mas um constru-tor diferente pode ser utilizado sem nenhum tipo de alteração. Para isso, será necessária a inclusão de uma anotação no nível da classe: @SuppressStaticInitializationFor. Atenção para o parâmetro dessa ano-tação! Esta recebe uma String e não uma classe. A justificativa para tal é de que se for passada uma classe esta será carregada no classloader e inviabilizará a desabilitação do bloco estático.

Como tratamos de uma readequação do sistema, o ponto negativo nesta abordagem é que as chances de alterarmos não apenas classes e métodos, mas também a localização de pacotes é muito alta. Se essa alteração ocorrer teremos um erro em tempo de execução dos testes. Para o pessoal que está acostumado a lidar com linguagens dinâmicas como Groovy e Ruby isso não será exatamente um problema.

estático. Repare que na saída do console, na Listagem 16, não consta o texto informando que passou pelo inicializador estático. Compare as saídas nas Listagens 4 e 11. Na Listagem 11 o texto: “Execução da Inicialização Estática de SLClient.” não é exibido.

Listagem 10. Suprimindo a inicialização estática – SLClient_ StaticInitializer_Test.java.

Listagem 11. Saída produzida pela execução da classe de teste: SLClient_StaticInitializer_Test.

@RunWith(PowerMockRunner.class)

@SuppressStaticInitializationFor(“mj.teste.servicelocator.client.SLClient”)

publicclass SLClient_StaticInitializer_Test {

@Test

publicvoidstaticInitialization_Suppressed_Test() throws Exception { SLClient client = newSLClient();

assertNotNull(client); }

}

Constructor de Base Client

Chamada a um WS informando que algum cliente está ativo .. mj.teste.servicelocator.client.SLClient

Construtor de StClient

Chamada a um WS informando que o cliente Cliente está ativo .. mj.teste.servicelocator.client.SLClient

&ODBQTVMBNFOUP BMUFSBÎÍPEFFTUBEPEF

objetos

O PowerMock possui uma classe chamada Whitebox, que é a respon-sável por fazer o "serviço sujo" de burlar o encapsulamento. Ou seja, li-teralmente desconsidera toda a modelagem (ou a falta dela) do sistema. Há quem defenda que a utilização de Whitebox é comparada à execu-ção de um projeto utilizando o Maven com a diretiva -DskipTests, isso é jogo sujo!

Mas a verdade é que usando Whitebox é possível criar uma classe utilizando o construtor privado da mesma, poderíamos, por exemplo, suprimir a funcionalidade de um singleton. Também é possível chamar um método privado, como mostrado anteriormente. A Listagem 18 faz todo este “trabalho sujo”.

Na Listagem 12 é apresentado o código da nossa classe Cliente. Ela não está tão ruim, mas ainda assim a classe contém o cálculo de idade que é feito baseado no conteúdo da variável yearBorn.

Queremos externalizar o método privado calculateAge(), mas este método está intimamente ligado à classe, fazendo uso da variável de instância yearBorn. Portanto, precisamos assegurar que o comporta-mento deste método esteja 100% testado para depois implementar em outra classe, onde o cálculo de idade pode ser reutilizado para outras localidades do código.

(7)

-JTUBHFN&TUSVUVSBEFEBEPTEF$MJFOUF$MJFOUF70KBWB publicclass ClientVO {

privatestatic String DEFAULT_NAME = “NOME PADRAO”;

private String name; private String address; private Integer yearBorn; private Integer age;

publicClientVO(String name, String address, Integer yearBorn) { this.setName(name);

this.setAddress(address); this.setYearBorn(yearBorn); } @SuppressWarnings(“unused”) privateClientVO() { }

privateintcalculateAge() { int age = 0;

try {

Calendar cal = Calendar.getInstance(); int thisYear = cal.get(Calendar.YEAR); age = thisYear - this.yearBorn; } catch (Exception e) { // this is absurd!

System.err.println(“Ups, erro aqui”); }

return age; }

public String getName() { if (name != null) { returnthis.name; } else {

return ClientVO.DEFAULT_NAME; }

}

//outros gets e sets

}

: : www.mundoj.com.br : :

-JTUBHFN  6UJMJ[BÎÍP EB 8IJUFCPY o $MJFOU70@1SJWBUF-Constructor_Test.java.

@RunWith(PowerMockRunner.class)

@PrepareForTest( { Calendar.class, ClientVO.class })

publicclass ClientVO_PrivateConstructor_Test {

@Mock Calendar cal;

@Before

publicvoidsetUp() {

when(cal.get(Calendar.YEAR)).thenReturn(Integer.valueOf(2060));

PowerMockito.mockStatic(Calendar.class); when(Calendar.getInstance()).thenReturn(cal); }

@Test

publicvoidfine_Test() throws Exception { final String name = “nome”; final String address = “endereco”; final Integer yearBorn = 2000;

ClientVO client = newClientVO(name, address, yearBorn);

assertTrue(60 == client.getAge().longValue()); }

@Test

publicvoidprivConstructor_privMethod_Test() throws Exception {

ClientVO client = Whitebox.invokeConstructor(ClientVO.class); Integer age = Whitebox.invokeMethod(client, “calculateAge”);

assertTrue(0 == age); }

@Test

publicvoidprivConstructor_changeState_Test() throws Exception {

ClientVO client = Whitebox.invokeConstructor(ClientVO.class);

Integer age = Whitebox.getInternalState(client, “age”); assertNull(age);

Whitebox.setInternalState(client, “yearBorn”, 2000); assertTrue(2000 == client.getYearBorn());

age = Whitebox.invokeMethod(client, “calculateAge”); assertTrue(60 == age);

} }

O primeiro passo é configurar o calendário para que, quando questionado quanto ao ano atual, este responda que é 2060, sempre. Esta configuração está feita no método setUp().Estamos fazendo utilização do método mockStatic() junto com when(). O primeiro teste é apenas para nos certificarmos que tudo está OK, que o cálculo de idade está sendo feito quando a classe é criada utilizando o único construtor disponível.

O segundo método de teste (privConstructor_privMethod_Test()) faz uso da Whitebox para chamar o construtor padrão (invoke-Constructor()) e o método calculateAge(invokeMethod()). Note que ambos são métodos privados e ainda assim a classe Whitebox é capaz de invocar estes métodos. Como o construtor padrão não JOWPDB P NÏUPEP TFU:FBS#PSO RVF Ï SFTQPOTÈWFM QPS DBMDVMBS B idade e atualizar o campo age, este tem seu valor zerado.

O último método de teste, privConstructor_changeState_Test(), além de instanciar a classe através do construtor privado e chamar

o método privado calculateAge(), também altera o valor de um campo privado yearBorn, através da chamada: Whitebox.setInter-nalState(). Com este campo preenchido, o resultado da chamada ao método calculateAge() deverá ser diferente de zero.

(FOFSBMJ[BÎÍP

Algumas vezes a implementação de um único teste nos leva à escrita de algumas dezenas de códigos para a configuração de um ambiente.

(8)

com @Test, o JUnit executará o código dentro do método anotado com @Before. Muito útil para configurações genéricas dentro do mesmo cenário de teste, ou mesmo para manter o código de teste livre de configurações.

Tomando o nosso último código como exemplo, Listagem 13, vamos fazer algumas modificações a fim de generalizarmos a configuração do cenário de testes.

Para os três testes na Listagem 13, é necessário que sempre que o ano, solicitado através da classe Calendar, responda com o valor 2060. Na implementação anterior colocamos este comportamento dentro do método setUp(), anotado com @Before.

No entanto, gostaríamos de externalizar cada um dos métodos de teste. Seja porque o código de teste ficou muito extenso, ou o obje-tivo dos testes não esteja vinculado, não faz mais sentido mantê-los na mesma classe. Mesmo assim, precisamos de um comportamento único da classe Calendar.

Existem várias opções de solução para este problema. Com Java puro, herança seria o mais indicado, ou então a implementação de um método utilitário que exportasse a data desejada. Nossa solução será utilizando MockPolicy, uma funcionalidade do Powermock. MockPolicies são implementações genéricas que serão inseridas na classe referenciada pela anotação @MockPolicy. Como parâmetro, a anotação recebe um array de objetos que implementem a interface PowerMockPolicy.

Os métodos que devem ser implementados – applyClassLoading-Policy e applyInterceptionapplyClassLoading-Policy – serão invocados sempre antes do método anotado com @Test, funciona de forma similar ao @Before. 7FKBOPRVBESPBEFmOJÎÍPQBSBDBEBVNEPTNÏUPEPT

Na Listagem 14 você pode ver a implementação de um MockPolicy para o nosso último exemplo, a simulação da classe Calendar e na Listagem 15 você confere a utilização desta Policy.

ApplyClassiLoadingPolicy

Informa ao classloader do PowerMock quais classes devem ser modificadas. Neste método devem ser informadas as classes que deveriam constar

em @prepareForTest. ApplyInterceptionPolicy

Efetivamente altera o comportamento das classes informadas no método anterior

(applyClassLoadingPolicy)

2VBESP.ÏUPEPTBTFSFNJNQMFNFOUBEPTVUJMJ[BOEP.PDL1PMJDZ

Listagem 15. Utilização do MockPolicy para a classe Calendar.

@Override

publicvoidapplyClassLoadingPolicy(MockPolicyClassLoadingSettings settings) {

settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader( Calendar.class.getName());

} @Override

publicvoidapplyInterceptionPolicy(MockPolicyInterceptionSettings settings) {

Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, 2060);

final Method getInstance = Whitebox.getMethod(Calendar.class, “getInstance”);

settings.stubMethod(getInstance, cal); } } @RunWith(PowerMockRunner.class) @PrepareForTest(ClientVO.class) @MockPolicy(CalendarYear2kPolicy.class)

publicclass ClientVO_MockPolicy_Fine_Test {

@Test

publicvoidfine_Test() throws Exception { final String name = “nome”; final String address = “endereco”; final Integer yearBorn = 2000;

ClientVO client = newClientVO(name, address, yearBorn);

assertTrue(60 == client.getAge().longValue()); }

}

Para Saber Mais

Evolução do Design através de Testes e o TDD – Edição 41 – Autor: Lucas Souza e Paulo Silveira

Automação de Testes de Aceitação com Cucumber e JRuby – Edição 40 – Autor: Demetrius Nunes

Automatização de testes de persistência com FIT, DBUnit e HSQLDB – Edição 38 – Autor: Alexandre Gazola

Testes de Unidade Avançados com o JMock 2 – Edição 31 – Autor: Eduardo Guerra

Testes unitários para camadas de negócios no mundo real – Edição 23

(9)

Considerações finais

Manter ou estender um código desenvolvido nos primórdios da lin-guagem Java pode ser algo muito difícil, tanto para o desenvolvedor quanto para a empresa, principalmente considerando a alta rotativida-de pela insatisfação do programador em lidar com código “estagnado” ou mesmo quanto ao retrabalho em desenvolver funcionalidades disponíveis em versões mais recentes de frameworks ou bibliotecas de desenvolvimento. Utilizando técnicas de refactoring, com responsabi-lidade, testando sempre antes de aprimorar determinada funcionalida-de, é uma alternativa atraente na batalha de manter o código sempre atualizado, pronto para a utilização dos melhores e mais modernos frameworks e com o mínimo impacto no produto final.

t1PXFNPDLoIUUQDPEFHPPHMFDPNQQPXFSNPDL t.PDLJUPoIUUQDPEFHPPHMFDPNQNPDLJUP t+6OJUoIUUQXXXKVOJUPSH t&DM&NNBo$PEF$PWFSBHFoIUUQXXXFDMFNNBPSH t&BTZ.PDLoIUUQXXXFBTZNPDLPSH t+BWBTTJTUoIUUQXXXKCPTTPSHKBWBTTJTU t+BZ8BZoIUUQXXXKBZXBZDPN Livros t$MFBO$PEFo")BOECPPLPG"HJMF4PGUXBSF$SBGUTNBOTIJQo#PC.BSUJO t8PSLJOH&õFDUJWFMZXJUI-FHBDZ$PEFo.JDIBFM$'FBUIFST Web t1SJODÓQJPEB3FTQPOTBCJMJEBEFÁOJDBIUUQFOXJLJQFEJBPSHXJLJ4JOHMF@SFTQPOTJ-CJMJUZ@QSJODJQMF t-FJEF%FNFUFSIUUQFOXJLJQFEJBPSHXJLJ-BX@PG@%FNFUFS t5FTU%SJWFO%FWFMPQNFOUIUUQQUXJLJQFEJBPSHXJLJ5FTU@%SJWFO@%FWFMPQNFOU t 5PP .BOZ 5FTUT o IUUQHPPHMFUFTUJOHCMPHTQPUDPNJONPWJFBNBEFVT austrian-emperor.html t$SJBÎÍPEFVN1SPYZoIUUQKBWBTVODPNKTFEPDTHVJEFSFnFDUJPOQSPYZ html

Referências

: : www.mundoj.com.br : :

0TNBMFGÓDJPTEP4JOHMFUPO

Esta afirmação é polêmica. De qualquer forma um Singleton não trata apenas de um assunto, ele trata no mínimo de dois. E esta duplicidade de papéis já é o suficiente para considerá-lo algo não adequado no desenvolvimento de sistemas.

Alguns outros motivadores a serem considerados: 6NBÞOJDBJOTUÉODJBQPS+7.

Isto é uma verdade, assim como qualquer variável estática um TJOHMFUPOÏDBSSFHBEPBQFOBTVNBWF[QPS+7.0TQSPCMFNBT começam a aparecer quando surge a necessidade de instalar o software desenvolvido com alguns singletons em um am-biente com cluster. O sistema terá o mesmo comportamento, BQFOBT VNB JOTUÉODJB EB DMBTTF QPS +7.  OP FOUBOUP BHPSB UFSFNPTOPNÓOJNPEVBT+7.T MPHP EVBTJOTUÉODJBTEBDMBTTF na aplicação, e isto pode não ser o adequado.

Ligação forte entre as classes.

Um dos princípios da testabilidade é o baixo acoplamento entre as classes. O baixo acoplamento permite que os objetos sejam “mockados” de acordo com o cenário que se deseja testar. Utilizando um singleton você está ligando fortemente à implementação da classe com o comportamento do Singleton. Não é impossível, mas ao menos muito mais trabalhoso de

.PDL0CKFDUT

Mock objects são objetos que simulam comportamento de ob-jetos reais. Frequentemente utilizados no desenvolvimento de testes ou quando existe integração entre sistemas e um deles ainda não está disponível para tal integração.

Mock Objects possuem características programáveis, como: t 7BMPSFTQSFWJBNFOUFEFmOJEPTOPSFUPSOPEFVNNÏUPEP t 2VBOUJEBEFEFWF[FTRVFVNNÏUPEPEFWFTFSFYFDVUBEP t 7FSJmDBÎÍPEFFYFDVÎÍP

Princípio da Responsabilidade Única

O conceito da Responsabilidade Única prega que cada objeto deve ter apenas uma responsabilidade e que esta responsabili-dade deve estar completamente encapsulada na classe. Todos os serviços devem estar alinhados com a responsabilidade daquela classe.

Este termo foi introduzido por Robert Martin – Uncle Bob

Lei de Demeter

A Lei de Demeter é um caso prático de baixo acoplamento. O conceito básico é de que dado um objeto, o mínimo necessá-rio de conhecimento sobre sua estrutura e propriedades deve ser conhecido.

t/ÍPGBMFDPNFTUSBOIPT

Minha mãe já dizia: “Não fale com estranhos”. E é exatamente isso que diz a Lei de Demeter.

A um objeto só é permitido acessar recursos de objetos conhe-cidos. Ou seja, objetos declarados dentro da própria classe ou do próprio método.

1. Exemplo de código “falando com estranhos”

Estado estado = cadastroDePessoa.getEndereco().getCidade(). getEstado();

2. Exemplo de código sem “falar com estranhos” Estado estado = cadastroDePessoa.getEstado();

Existem vantagens e desvantagens em se aplicar a Lei de Demeter. Uma grande vantagem é que o código fica menos dependente da estrutura interna, mais legível e com isso “ma-nutenível”.

Uma desvantagem é que algumas vezes é necessário escrever mais código apenas para externalizar algumas funcionalidades – Wrappers – como feito no exemplo.

Referências

Documentos relacionados

Por vezes, o localizador necessita de alterar não só o texto como também possíveis imagens ou a forma como estas são apresentadas, sendo o exemplo mais óbvio o caso de

Equipamentos de emergência imediatamente acessíveis, com instruções de utilização. Assegurar-se que os lava- olhos e os chuveiros de segurança estejam próximos ao local de

A seqüência analítica • Definição do problema • Escolha do método • Amostragem • Pré-tratamento da amostra • Medida • Calibração • Avaliação •

6 Consideraremos que a narrativa de Lewis Carroll oscila ficcionalmente entre o maravilhoso e o fantástico, chegando mesmo a sugerir-se com aspectos do estranho,

Na sua qualidade de instituição responsável pela organização do processo de seleção, o Secretariado-Geral do Conselho garante que os dados pessoais são tratados nos termos do

IV - a inexistência de risco de agravamento de processos como enchentes, erosão ou movimentos acidentais de massa rochosa. 4 - Toda obra, plano, atividade ou projeto de

A prova do ENADE/2011, aplicada aos estudantes da Área de Tecnologia em Redes de Computadores, com duração total de 4 horas, apresentou questões discursivas e de múltipla

Não houve diferença significativa para as variáveis comprimento de raízes comerciais e diâmetro de raízes comerciais; os clones 06 e 14 e a cultivar sergipana apresentaram