• Nenhum resultado encontrado

Fixture-Factory. Criando objetos para seus testes. Como criar objetos através de templates para serem utilizados como massa de dados em seus testes.

N/A
N/A
Protected

Academic year: 2021

Share "Fixture-Factory. Criando objetos para seus testes. Como criar objetos através de templates para serem utilizados como massa de dados em seus testes."

Copied!
5
0
0

Texto

(1)

fixture-factory_

Começamos testando métodos simples, mas e quando dependemos

de objetos com determinados valores para realizar o teste, como

fa-zer? O Fixture-Factory é um framework que surgiu para auxiliar na

criação e manutenção de objetos utilizados nos testes, tudo isto de

uma maneira simples e intuitiva através da declaraçāo de

templa-tes.

Fixture-Factory

Criando objetos para seus testes

Como criar objetos através de templates para serem utilizados

como massa de dados em seus testes.

J

á faz algum tempo que a comunidade de desen-volvimento de software discute a necessidade de garantir a qualidade do que está sendo desenvolvi-do. Em busca disto surgiram algumas soluções como o uso de testes automatizados e TDD. Aplicar estes conceitos pode parecer um pouco complicado no início e uma das maiores dificuldades encontradas é de como criar os dados necessários para realizar os testes.

Uma das soluções mais utilizadas é o uso de Fix-tures. Neste artigo apresentaremos uma solução al-ternativa/complementar às Fixtures, que é o Fixture--Factory um framework que auxiliar na criação dos dados necessários quando e onde precisamos. Tudo isto através uma maneira programática, feito de pro-gramadores para propro-gramadores.

Fixtures

As Fixtures são utilizadas para especificar os dados que serão criados. Para realizar essa especifi-cação podem ser utilizados arquivos xml, json, yaml etc. Além de criar estes arquivos que dizem quais valores os objetos criados terão é necessário reali-zar algumas configurações para que tudo funcione (acesso a base de dados, por exemplo).

Após configurado, é possível utilizar esses dados para mockar resultados de métodos, consultas a base de dados, consultas webservices etc.

O grande problema das Fixtures é que elas aca-bam tornando-se difíceis de manter em longo prazo. Com o passar do tempo, a tendência é termos diver-sos arquivos de Fixtures e em alguns cadiver-sos até arqui-vos grandes demais, que causam grande dificuldade

(2)

Nykolas Laurentino de Lima | nykolas.lima@gmail.com É programador na Amil, trabalha com Java há mais de 4 anos. Certificado SCJP e SCWCD.

na manutenção. Além dis-so, os dados configurados nas Fixtures são em sua grande maioria hard-coded, o que muitas vezes leva o programador a escrever testes baseados nesses dados específicos. O proble-ma de testar baseado em dados fixos é que quando os dados mudarem, quando a aplicação for para produ-ção, por exemplo, o código pode não funcionar.

Exemplo de fixture usando YAML: valid_student:

id: 1

nome: Nykolas Laurentino de Lima email: nykolas.lima@gmail.com dataNascimento: 02/06/1991 mediaFinal: 7

Fixture-Factory

O Fixture-Factory é um framework que foi cria-do para facilitar a criação de objetos para serem uti-lizados nos testes.

Diferente das Fixtures, não é necessária nenhu-ma configuração adicional. Só é necessário descrever quais valores queremos que nossos objetos tenham e pronto. Além de não precisar de nenhuma configura-ção adicional, a definiconfigura-ção dos valores que serão utili-zados nos nossos objetos é feita programaticamente, ou seja, utilizando código Java. Sem arquivos XML, YAML, JSON ou qualquer outro tipo. O framework funciona de maneira similar ao Factory-Girl utiliza-do na criação de testes em Ruby.

Template

Para definirmos os valores que nossos objetos gerados terão é necessário escrevermos um template para eles. O template irá definir quais valores serão gerados para cada propriedade do nosso objeto, mas, diferentemente das Fixtures, os templates podem definir diversos valores possíveis para a mesma pro-priedade. Podemos gerar valores aleatórios baseados em uma lista de valores possíveis, por meio de ex-pressões regulares, períodos de data etc.

Uma das grandes vantagens na utilização de Templates e não de Fixtures estáticas é que criamos objetos que possuem valores que variam constan-temente, a cada execução do mesmo teste o objeto utilizado por ele pode ter valores diferentes. Desta maneira nossos testes tendem a ser menos atrelados a dados específicos e mais direcionados a padrões de dados que nós definimos como válidos.

Um exemplo muito comum de testes criados baseados em dados é quando dependemos de um período de data. Com o uso de Templates podemos definir que o valor de determinado atributo deve ser de 6 meses atrás, isto pode ser feito de uma maneira simples e fácil, o que com o uso Fixtures poderia de-mandar uma quantidade considerável de refactoring e mocks.

Criando o Template

A criação do template é bem simples, dizemos para qual classe desejamos criar o template, qual o nome dele e então configuramos os valores para as propriedades da classe. Vale lembrar que uma mes-ma classe pode ter diversos templates diferentes.

Listagem 1.

A Listagem 1 exemplifica a codificação de um template para a classe Client.

Fixture.of(Client.class).addTemplate(“geekClient”,

newRule() {{ add(“id”, 1L);

add(“name”, “Nykolas Laurentino de Lima”); add(“nickname”, “geek”);

add(“email”, “${nickname}@gmail.com”); add(“birthday”, newDate());

}}

).addTemplate(“nerdClient”, newRule() {{ add(“id”, 2L);

add(“name”, “Anderson Parra”); add(“nickname”,”nerd”);

add(“email”, “${nickname}@gmail.com”); add(“birthday”, newDate());

(3)

Na Listagem 1 nós criamos dois templates para a classe Client, um deles se chama geekClient e o ou-tro nerdClient. Note que podemos utilizar placehol-ders para criar valores de uma propriedade a partir de outra propriedade, como foi feito na propriedade e-mail. Mais abaixo será explicado o uso de funções para gerar valores dinamicamente na criação do ob-jeto.

Para obter uma instância do template criado só precisamos informar a classe desejada e o nome do template:

Listagem 2

. A Listagem 2 exemplifica a codificação para obter uma instância da classe Client através do template “geekClient“.

Fixture.from(Client.class).gimme(“geekClient”);

Um exemplo do objeto gerado para o template “geekClient” pode ser visto na figura 1.

Figura 1. Exemplo de objeto gerado para o template “geekClient”.

Relacionamentos

Tratar os relacionamentos entre os objetos tam-bém pode ser feito de maneira simples e fácil através das RelationFunctions.

Listagem 3

. A Listagem 3 exemplifica a codificação das Relation Functions one e has.

Fixture.of(Address.class).addTemplate(“brazilianAddress”,

newRule() {{ add(“id”, 1L);

add(“street”, “Paulista Avenue”); add(“city”, “São Paulo”); add(“state”, “SP”);

add(“country”, “Brazil”); add(“zipCode”, “0660800”); }});

Fixture.of(Phone.class).addTemplate( “brazilianPhoneNumber”, newRule() {{ add(“number”, “11 9999-9999”); }});

Fixture.of(Client.class).addTemplate(“geekClient”,

newRule() {{

//...

add(“address”, one(Address.class, “brazilianAddress”)); add(“phones”, has(3).of(Phone.class,

“brazilianPhoneNumber”));

//...

}});

A função one irá criar uma instância da classe Address utilizando o template “brazilianAddress” que foi criado logo acima.

A propriedade phones é uma lista de telefones, portanto utilizamos a função has e dizemos para ela quantas instâncias nós queremos, de qual classe e qual template será utilizado.

Agora ao obtermos uma instância do template “geekClient”, automaticamente serão criados os re-lacionamentos com Address e Phone utilizando os templates declarados.

Um exemplo dos relacionamentos criados pode ser visto na figura 2.

Functions

As Functions são utilizadas para gerar valores di-nâmicos para as propriedades de um objeto. As Rela-tionFunctions explicadas na seção anterior exempli-ficam a geração de objetos para os relacionamentos, mas existem outras funções para auxiliar na criação de valores simples.

Random

A função random é utilizada para escolher um valor aleatoriamente dentre uma lista de possíveis valores.

(4)

Listagem 4.

A Listagem 4 exemplifica a codificação da função random para geração de valores aleatórios.

Fixture.of(Client.class).addTemplate(“geekClient”,

newRule() {{ //...

add(“name”, random(“Nykolas Laurentino de Lima”, “Anderson Parra”));

//...

}});

Ao obter uma instância do template “geekClient” a propriedade name terá seu valor gerado aleatoria-mente conforme configurado.

Regex

A função regex pode ser utilizada para gerar valo-res baseados em uma expvalo-ressão regex.

Listagem 5.

Utilizando expressões regulares para gerar o valor da propriedade.

Fixture.of(Phone.class).

addTemplate(“brazilianPhoneNumber’, new Rule() {{

add(“number”, regex(“(\\d{2}) (\\d{4}) - (\\d{4})”)); }});

Um exemplo do resultado da função regex pode ser visto na figura 3.

Figura 3. Exemplo de valor gerado através da função regex.

Date

Para tratamento de datas podemos utilizar as funções beforeDate, afterDate, randomDate e ins-tant. Estas funções facilitam a manipulação de datas através de uma interface simples e intuitiva.

Listagem 6.

Utilizando as funções de datas para gerar o valor das propriedades.

Fixture.of(Client.class).addTemplate(“geekClient”,

newRule() {{ //...

add(“birthday”, randomDate(“1980-04-15”, “1985-11-07”, newSimpleDateFormat( “yyyy-MM-dd”)));

add(“contractBegginingDate”, beforeDate(“2011-04-15”,

newSimpleDateFormat(“yyyy-MM-dd”))); add(“contractEndingDate”, afterDate(“2011-04-15”), newSimpleDateFormat(“yyyy-MM-dd”));

add(“lastLoginDate”, instant(“now”)); //...

}});

Name

As funções name auxiliam na criação de nomes aleatórios.

Listagem 7.

Utilizando as funções de nomes para gerar nomes aleatórios.

Fixture.of(Client.class).addTemplate(“geekClient”,

newRule(){{ //...

add(“name”, firstName()); add(“lastName”, lastName()); //...

}});

Um exemplo dos valores gerados pelas funções name e lastName pode ser visto na figura 4.

Figura 4. Exemplo de valores gerados através das funções name e

lastName.

Refatorando nossos templates

Podemos agora refatorar os templates para Client, Address e Phone para utilizarem as functions. Deste modo os valores serão gerados dinamicamente no momento em que nossos objetos forem utilizados nos testes.

Listagem 8

.Template para Client refatorado. Fixture.of(Client.class).addTemplate(“geekAndNerdClient”,

newRule() {{

add(“id”, random(Long.class, range(1L, 200L))); add(“name”, random(“Nykolas Laurentino de Lima”, “Anderson Parra”));

add(“lastName”, lastName());

add(“nickname”, random(“nerd”, “geek”)); add(“email”, “${nickname}@gmail.com”); add(“birthday”, instant(“18 years ago”));

(5)

add(“phones”, has(3).of(Phone.class, “brazilianPhoneNumber”)); }});

Listagem 9.

Template para Address refatorado. Fixture.of(Address.class).addTemplate(“brazilianAddress”,

newRule() {{

add(“id”, random(Long.class, range(1L, 100L))); add(“street”, random(“Paulista Avenue”, “Ibirapuera avenue”));

add(“city”, “São Paulo”); add(“state”, “SP”); add(“country”, “Brazil”);

add(“zipCode”, random(“0660800”, “17720000”)); }});

Listagem 10.

Template para Phone refatorado. Fixture.of(Phone.class).

addTemplate(“brazilianPhoneNumber”,

newRule() {{

add(“number”, regex(“(\\d{2}) \\d{4}-\\d{4}”)); }});

Após o refactoring é possível notar a melhora no código, na definição dos valores que serão gerados e o ganho que isto traz na abrangência e diversidade de dados que serão testados.

Organizando seus templates

É possível criar os templates em qualquer lugar. No @Before de uma classe de testes ou até mesmo dentro do próprio método que realiza o teste. Entre-tanto, uma boa prática para a organização dos tem-plates é a criação de classes separadas para declarar os templates de cada “módulo” do seu sistema.

Nos meus projetos pessoais eu costumo criar uma classe TemplateLoader que contém uma inner class para cada entidade ou módulo do meu sistema.

Listagem 11.

Exemplo de TemplateLoader responsá-vel pela criação dos templates.

publicclass TemplateLoader { publicstaticvoidloadTemplates() {

TemplateLoader.ClientTemplate.loadTemplates(); }

privatestaticclass ClientTemplate { publicstaticvoidloadTemplates() {

Fixture.of(Client.class).addTemplate(“geekClient”, newRule() {{

//...

}});

//Declaração dos outros templates relacionados a

// Client

} } }

Após criar o TemplateLoader é possível chamar o mé-todo “TemplateLoader.loadTemplates” para carregar todos os templates ou carregar somente os templa-tes desejados através do método “TemplateLoader. ClientTemplate.loadTemplates()”.

Considerações finais

O Fixture-Factory oferece uma DSL (domain--specific language) simples e intuitiva para criação de objetos para nossos testes.

Com o uso das Relation Functions, configurar os relacionamentos entre as entidades torna-se uma tarefa fácil. Para relacionamentos one-to-one basta utilizar a função one dizendo a classe e o nome do template a ser utilizado. Para relacionamentos one--to-many a função has recebe a classe, a quantidade de instâncias desejadas e o nome do template a ser utilizado.

Comparado com as Fixtures, o Fixture-Factory possui a vantagem de sua utilização e configuração ser feita 100% com código Java, não precisando de ar-quivos XML ou JSON. Com isto a leitura, debug, iden-tificação de erros e refactoring são muito melhores (caso você erre algo na sintaxe, o compilador vai te avisar).

Quando utilizamos fixtures ou criamos nossos objetos “na mão” no momento do teste, estes objetos possuem sempre os mesmos valores fixos que foram configurados. Isto pode influenciar o programador a escrever testes baseados nesses dados hard-coded, o que pode acabar “escondendo” problemas na imple-mentação do código que poderiam ser identificados no momento do teste caso os valores fossem gerados através de padrões configurados no Template do Fix-ture-Factory. Este é um exemplo claro da vantagem do uso de Templates e das Functions do framework para geração dos objetos.

> DO repositório do Fixture-Factory no github é: https:// github.com/aparra/fixture-factory

> Dúvidas, sugestões ou qualquer outro assunto relacionado ao framework podem ser enviadas para a lista Mailing List(https://groups.google.com/ forum/?fromgroups#!forum/fixture-factory). > Fontes utilizados no artigo: https://github.com/ nykolaslima/fixture-factory-mundoj

> Factory-Girl, solução parecida utilizada em Ruby: https://github.com/thoughtbot/factory_girl

Referências

Documentos relacionados

de científica, tecnológica e de inovação nos estados da Amazônia Legal, para promover o desenvolvimento sus- tentável mediante o fortalecimento das instituições esta- duais

Os resultados deste trabalho mostram que o tempo médio de jejum realizado é superior ao prescrito, sendo aqueles que realizam a operação no período da tarde foram submetidos a

Sabendo-se que o tamanho e o peso das plaquetas, hemácias e leucócitos determinam o protocolo mais efetivo para concentrar plaquetas em cada espécie (LÓPEZ et al.,

At the baseline and after one year, changes in weight gain, body composition by the bioimpedance electric method, resting energy expenditure (REE) by the indirect calorimetry

Apenas foram submetidos aos protocolos de provocação e/ou dessensibilização com AAS, os pacientes com asma controlada e, portanto, os pacientes que tinham asma grave não

Métodos: Por meio de um estudo seccional, foram pesquisados os dados clínicos e laboratoriais (alanina aminotransferase [ALT], aspartato aminotransferase [AST], fosfatase

Paralelo a este trabalho, desenvolvemos uma pesquisa sobre a alfabetização no Rio Grande do Norte, que objetivou compreender o fazer pedagógico no que concerne aos ensinamentos

A maioria, ainda que em menor número do que nos pontos anteriores, também considera que o acesso ao espaço exterior de ar livre, a companhia de outros animais e cuidados de