Você está na página 1de 5

fixture-factory_

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

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.

J á faz algum tempo que a comunidade de desen-


volvimento de software discute a necessidade de
garantir a qualidade do que está sendo desenvolvi-
Fixtures
As Fixtures são utilizadas para especificar os
dados que serão criados. Para realizar essa especifi-
do. Em busca disto surgiram algumas soluções como cação podem ser utilizados arquivos xml, json, yaml
o uso de testes automatizados e TDD. Aplicar estes etc. Além de criar estes arquivos que dizem quais
conceitos pode parecer um pouco complicado no valores os objetos criados terão é necessário reali-
início e uma das maiores dificuldades encontradas é zar algumas configurações para que tudo funcione
de como criar os dados necessários para realizar os (acesso a base de dados, por exemplo).
testes. Após configurado, é possível utilizar esses dados
Uma das soluções mais utilizadas é o uso de Fix- para mockar resultados de métodos, consultas a base
tures. Neste artigo apresentaremos uma solução al- de dados, consultas webservices etc.
ternativa/complementar às Fixtures, que é o Fixture- O grande problema das Fixtures é que elas aca-
-Factory um framework que auxiliar na criação dos bam tornando-se difíceis de manter em longo prazo.
dados necessários quando e onde precisamos. Tudo Com o passar do tempo, a tendência é termos diver-
isto através uma maneira programática, feito de pro- sos arquivos de Fixtures e em alguns casos até arqui-
gramadores para programadores. vos grandes demais, que causam grande dificuldade

/ 26
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- Uma das grandes vantagens na utilização de


so, os dados configurados Templates e não de Fixtures estáticas é que criamos
nas Fixtures são em sua grande maioria hard-coded, objetos que possuem valores que variam constan-
o que muitas vezes leva o programador a escrever temente, a cada execução do mesmo teste o objeto
testes baseados nesses dados específicos. O proble- utilizado por ele pode ter valores diferentes. Desta
ma de testar baseado em dados fixos é que quando os maneira nossos testes tendem a ser menos atrelados
dados mudarem, quando a aplicação for para produ- a dados específicos e mais direcionados a padrões de
ção, por exemplo, o código pode não funcionar. dados que nós definimos como válidos.
Exemplo de fixture usando YAML: Um exemplo muito comum de testes criados
valid_student:
baseados em dados é quando dependemos de um
id: 1 período de data. Com o uso de Templates podemos
nome: Nykolas Laurentino de Lima definir que o valor de determinado atributo deve ser
email: nykolas.lima@gmail.com de 6 meses atrás, isto pode ser feito de uma maneira
dataNascimento: 02/06/1991 simples e fácil, o que com o uso Fixtures poderia de-
mediaFinal: 7 mandar uma quantidade considerável de refactoring
e mocks.

Fixture-Factory Criando o Template


O Fixture-Factory é um framework que foi cria- A criação do template é bem simples, dizemos
do para facilitar a criação de objetos para serem uti- para qual classe desejamos criar o template, qual o
lizados nos testes. nome dele e então configuramos os valores para as
Diferente das Fixtures, não é necessária nenhu- propriedades da classe. Vale lembrar que uma mes-
ma configuração adicional. Só é necessário descrever ma classe pode ter diversos templates diferentes.
quais valores queremos que nossos objetos tenham e
pronto. Além de não precisar de nenhuma configura- Listagem 1. A Listagem 1 exemplifica a codificação
ção adicional, a definição dos valores que serão utili- de um template para a classe Client.
zados nos nossos objetos é feita programaticamente,
ou seja, utilizando código Java. Sem arquivos XML,
YAML, JSON ou qualquer outro tipo. O framework Fixture.of(Client.class).addTemplate(“geekClient”,
new Rule() {{
funciona de maneira similar ao Factory-Girl utiliza-
add(“id”, 1L);
do na criação de testes em Ruby. add(“name”, “Nykolas Laurentino de Lima”);
add(“nickname”, “geek”);
Template add(“email”, “${nickname}@gmail.com”);
Para definirmos os valores que nossos objetos add(“birthday”, new Date());
gerados terão é necessário escrevermos um template }}
).addTemplate(“nerdClient”, new Rule() {{
para eles. O template irá definir quais valores serão
add(“id”, 2L);
gerados para cada propriedade do nosso objeto, mas, add(“name”, “Anderson Parra”);
diferentemente das Fixtures, os templates podem add(“nickname”,”nerd”);
definir diversos valores possíveis para a mesma pro- add(“email”, “${nickname}@gmail.com”);
priedade. Podemos gerar valores aleatórios baseados add(“birthday”, new Date());
em uma lista de valores possíveis, por meio de ex- }});
pressões regulares, períodos de data etc.

27 \
Na Listagem 1 nós criamos dois templates para a
classe Client, um deles se chama geekClient e o ou- add(“country”, “Brazil”);
tro nerdClient. Note que podemos utilizar placehol- add(“zipCode”, “0660800”);
ders para criar valores de uma propriedade a partir }});
de outra propriedade, como foi feito na propriedade Fixture.of(Phone.class).addTemplate(
“brazilianPhoneNumber”, new Rule() {{
e-mail. Mais abaixo será explicado o uso de funções
add(“number”, “11 9999-9999”);
para gerar valores dinamicamente na criação do ob- }});
jeto. Fixture.of(Client.class).addTemplate(“geekClient”,
Para obter uma instância do template criado só new Rule() {{
precisamos informar a classe desejada e o nome do //...
template: add(“address”, one(Address.class, “brazilianAddress”));
add(“phones”, has(3).of(Phone.class,
Listagem 2. A Listagem 2 exemplifica a codificação “brazilianPhoneNumber”));
para obter uma instância da classe Client através do //...
template “geekClient“. }});

Fixture.from(Client.class).gimme(“geekClient”);
A função one irá criar uma instância da classe Address
utilizando o template “brazilianAddress” que foi
Um exemplo do objeto gerado para o template criado logo acima.
“geekClient” pode ser visto na figura 1. 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
Figura 1. Exemplo de objeto gerado para o template “geekClient”. ser visto na figura 2.

Functions
Relacionamentos As Functions são utilizadas para gerar valores di-
Tratar os relacionamentos entre os objetos tam- nâmicos para as propriedades de um objeto. As Rela-
bém pode ser feito de maneira simples e fácil através tionFunctions explicadas na seção anterior exempli-
das RelationFunctions. ficam a geração de objetos para os relacionamentos,
mas existem outras funções para auxiliar na criação
Listagem 3. A Listagem 3 exemplifica a codificação
de valores simples.
das Relation Functions one e has.

Fixture.of(Address.class).addTemplate(“brazilianAddress”, Random
new Rule() {{ A função random é utilizada para escolher um
add(“id”, 1L); valor aleatoriamente dentre uma lista de possíveis
add(“street”, “Paulista Avenue”); valores.
add(“city”, “São Paulo”);
add(“state”, “SP”);

Figura 2. Exemplo de objeto gerado utilizando as funções de relacionamento one e has.

/ 28
Listagem 4. A Listagem 4 exemplifica a codificação
da função random para geração de valores aleatórios. new SimpleDateFormat(“yyyy-MM-dd”)));
add(“contractEndingDate”, afterDate(“2011-04-15”),
new SimpleDateFormat(“yyyy-MM-dd”));
Fixture.of(Client.class).addTemplate(“geekClient”, add(“lastLoginDate”, instant(“now”));
new Rule() {{ //...
//... }});
add(“name”, random(“Nykolas Laurentino de Lima”,
“Anderson Parra”));
//...
}});
Name
As funções name auxiliam na criação de nomes
aleatórios.
Ao obter uma instância do template “geekClient”
a propriedade name terá seu valor gerado aleatoria- Listagem 7. Utilizando as funções de nomes para
mente conforme configurado. gerar nomes aleatórios.

Fixture.of(Client.class).addTemplate(“geekClient”,
Regex new Rule(){{
A função regex pode ser utilizada para gerar valo- //...
res baseados em uma expressão regex. add(“name”, firstName());
add(“lastName”, lastName());
Listagem 5. Utilizando expressões regulares para //...
gerar o valor da propriedade. }});

Fixture.of(Phone.class). Um exemplo dos valores gerados pelas funções


addTemplate(“brazilianPhoneNumber’, name e lastName pode ser visto na figura 4.
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 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
Figura 3. Exemplo de valor gerado através da função regex. modo os valores serão gerados dinamicamente no
momento em que nossos objetos forem utilizados nos
testes.
Date
Para tratamento de datas podemos utilizar as
funções beforeDate, afterDate, randomDate e ins- Listagem 8.Template para Client refatorado.
tant. Estas funções facilitam a manipulação de datas
através de uma interface simples e intuitiva.
Fixture.of(Client.class).addTemplate(“geekAndNerdClient”,
Listagem 6. Utilizando as funções de datas para new Rule() {{
gerar o valor das propriedades. add(“id”, random(Long.class, range(1L, 200L)));
add(“name”, random(“Nykolas Laurentino de Lima”,
Fixture.of(Client.class).addTemplate(“geekClient”, “Anderson Parra”));
new Rule() {{ add(“lastName”, lastName());
//... add(“nickname”, random(“nerd”, “geek”));
add(“birthday”, randomDate(“1980-04-15”, add(“email”, “${nickname}@gmail.com”);
“1985-11-07”, new SimpleDateFormat( add(“birthday”, instant(“18 years ago”));
“yyyy-MM-dd”))); add(“address”, one(Address.class, “brazilianAddress”));
add(“contractBegginingDate”, beforeDate(“2011-04-15”,

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

Listagem 9. Template para Address refatorado. Após criar o TemplateLoader é possível chamar o mé-
todo “TemplateLoader.loadTemplates” para carregar
Fixture.of(Address.class).addTemplate(“brazilianAddress”, todos os templates ou carregar somente os templa-
new Rule() {{ tes desejados através do método “TemplateLoader.
add(“id”, random(Long.class, range(1L, 100L))); ClientTemplate.loadTemplates()”.
add(“street”, random(“Paulista Avenue”,
“Ibirapuera avenue”));
add(“city”, “São Paulo”);
Considerações finais
O Fixture-Factory oferece uma DSL (domain-
add(“state”, “SP”);
add(“country”, “Brazil”); -specific language) simples e intuitiva para criação
add(“zipCode”, random(“0660800”, “17720000”)); 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
Listagem 10. Template para Phone refatorado. utilizar a função one dizendo a classe e o nome do
Fixture.of(Phone.class). template a ser utilizado. Para relacionamentos one-
addTemplate(“brazilianPhoneNumber”, -to-many a função has recebe a classe, a quantidade
new Rule() {{
de instâncias desejadas e o nome do template a ser
add(“number”, regex(“(\\d{2}) \\d{4}-\\d{4}”));
}});
utilizado.
Comparado com as Fixtures, o Fixture-Factory
Após o refactoring é possível notar a melhora no possui a vantagem de sua utilização e configuração
código, na definição dos valores que serão gerados e ser feita 100% com código Java, não precisando de ar-
o ganho que isto traz na abrangência e diversidade de quivos XML ou JSON. Com isto a leitura, debug, iden-
dados que serão testados. tificação de erros e refactoring são muito melhores
(caso você erre algo na sintaxe, o compilador vai te
Organizando seus templates avisar).
É possível criar os templates em qualquer lugar. Quando utilizamos fixtures ou criamos nossos
No @Before de uma classe de testes ou até mesmo objetos “na mão” no momento do teste, estes objetos
dentro do próprio método que realiza o teste. Entre- possuem sempre os mesmos valores fixos que foram
tanto, uma boa prática para a organização dos tem- configurados. Isto pode influenciar o programador a
plates é a criação de classes separadas para declarar escrever testes baseados nesses dados hard-coded, o
os templates de cada “módulo” do seu sistema. que pode acabar “escondendo” problemas na imple-
Nos meus projetos pessoais eu costumo criar mentação do código que poderiam ser identificados
uma classe TemplateLoader que contém uma inner no momento do teste caso os valores fossem gerados
class para cada entidade ou módulo do meu sistema. através de padrões configurados no Template do Fix-
ture-Factory. Este é um exemplo claro da vantagem
Listagem 11. Exemplo de TemplateLoader responsá- do uso de Templates e das Functions do framework
vel pela criação dos templates. para geração dos objetos.
/referências
public class TemplateLoader {
> DO repositório do Fixture-Factory no github é: https://
public static void loadTemplates() {
github.com/aparra/fixture-factory
TemplateLoader.ClientTemplate.loadTemplates();
} > Dúvidas, sugestões ou qualquer outro assunto
relacionado ao framework podem ser enviadas
private static class ClientTemplate { para a lista Mailing List(https://groups.google.com/
public static void loadTemplates() { forum/?fromgroups#!forum/fixture-factory).
Fixture.of(Client.class).addTemplate(“geekClient”,
> Fontes utilizados no artigo: https://github.com/
new Rule() {{
nykolaslima/fixture-factory-mundoj
//...
}}); > Factory-Girl, solução parecida utilizada em Ruby:
//Declaração dos outros templates relacionados a https://github.com/thoughtbot/factory_girl

/ 30