Criando cenários de teste com Fixture Factory

Escrever testes automatizados é com certeza um desafio, ainda mais para quem está começando. Mas, independente de conhecimento, montar cenários de teste é sempre uma tarefa que requer um certo esforço.

Essa é geralmente a parte mais chata de se escrever um teste. O programador precisa instanciar entidades (às vezes várias delas, pois uma pode ser composta por outro), passar valores específicos para elas, e etc. Em linguagens menos enxutas, como Java, isso costuma gastar algumas linhas de código de teste.

Para facilitar a vida, é comum que programadores apelem para classes especializadas, que ajudam a criar cenários de teste. Existem até padrões de projeto, como é o caso do Test Data Builder, que é um Builder (à la GoF) de cenários.

Um framework brasileiro que resolve bem esse problema é o Fixture-Factory. A ideia dele é justamente facilitar a criação de cenários. Como? Você ensina ele a criar objetos e, depois disso, apenas pede instâncias de cenários pra ele.

Veja, por exemplo, o código abaixo. Nele definimos duas fixtures (que é o nome que damos para “exemplos de cenários”) com o nome valido, e depois pedimos uma instância de Cliente:

Fixture.of(Cliente.class).addTemplate("valido", new Rule(){{
    add("id", random(Long.class, range(1L, 200L)));
    add("nome", random("Anderson Parra", "Arthur Hirata"));
    add("apelido", random("nerd", "geek"));
    add("email", "${apelido}@gmail.com");
    add("aniversario", instant("18 years ago"));
    add("endereco", fixture(Endereco.class, "valido"));
}});

Fixture.of(Endereco.class).addTemplate("valido", new Rule(){{
    add("id", random(Long.class, range(1L, 100L)));
    add("rua", random("Paulista Avenue", "Ibirapuera Avenue"));
    add("cidade", "São Paulo");
    add("estado", "${cidade}");
    add("pais", "Brasil");
    add("cep", random("06608000", "17720000"));
}});

Cliente cliente = Fixture.from(Cliente.class).gimme("valido");

Repare na quantidade de maneiras diferentes que você pode configurar sua fixture. O nome é randômico, o e-mail é baseado no outro atributo, o endereço vem de outra fixture, e etc. O framework é realmente bem completo, e é constantemente evoluído.

Há um tempo atrás, o Anderson Parra, um dos criadores do framework deu uma palestra aqui na Caelum sobre o assunto. Você pode vê-la aqui:

Se você deseja aprender mais sobre testes automatizados e TDD, pode consultar meu livro, ou os cursos online do Alura, além do nosso curso de práticas ágeis na Caelum.

19 Comentários

  1. Leonardo Comelli 08/10/2013 at 11:09 #

    Mauricio,

    Eu assisti a palestra do Parra na Caelum, achei muito interessante. Desde então estou incluindo o FixtureFactory nos meus testes e estou bem satisfeito.

    Na versão 2.2.0-SNAPSHOT é possível pedir uma instância persistida de Cliente, por exemplo:

    Fixture.from(Cliente.class, session).gimme(“valido”);

    O FF cria uma instância e invoca o método session.save(result);

    Com esta nova funcionalidade, eu estou tentando utilizar o FF nos testes dos DAO´s. O principal objetivo é centralizar e reduzir a criação da massa de dados que hoje é feita com alguns xmls enormes (via DBUnit).

    Assim que conseguir evoluir mais… eu compartilho minha experiência.

  2. Mauricio Aniche 08/10/2013 at 12:53 #

    Oi Leonardo,

    O FF é bem legal mesmo, e muito legal ver que está evoluindo! Torço para que esse post faça a galera contribuir ainda mais com o projeto!

    Um abraço!

  3. Phillip 08/10/2013 at 13:53 #

    Muito legal essa palestra, muita coisa interessante que poderei adotar nos meus testes.

  4. Nykolas Lima 09/10/2013 at 15:56 #

    Para quem tiver interesse, na edição 59 da revista MundoJ tem um artigo que explica detalhadamente como utilizar o Fixture-Factory para facilitar a criação de testes.

    Muito legal o post Aniche!

  5. Mauricio Aniche 09/10/2013 at 20:37 #

    Bom saber disso, Nykolas!

  6. damianijr 10/10/2013 at 10:47 #

    sensacional..
    parabéns pra galera que criou, ainda mais ver que eh br 🙂

    já estou considerando utilizar nos contextos de testes do projeto que estou…

  7. Stélio Moiane 11/10/2013 at 06:19 #

    Demais,

    Adoreia a idea, e ja coloquei num projectinho em desenvolvimento,

    Minhas calsses de testes eram gigantes por conta dos senários.

    So uma questão, alguem ja criou uma convenção de organização das fixtures? se sim como tem feito.

  8. Eudson Bambo 11/10/2013 at 12:23 #

    Vi com um colega aqui do trabalho, achei bastante interessante. Vou usar nos meus testes e ver como se encaixa.

    Para já vou ajudar a divulgar.
    Parabéns pelo projecto.

  9. Nykolas Lima 14/10/2013 at 16:23 #

    Stelio,

    Eu costumo criar uma classe para cada entidade do meu dominio.

    Ex: UsuarioTemplateLoader | CarroTemplateLoader

    E dentro destas classes vc define os seus templates.

    Para facilitar a criacao destas classes, voce pode implementar uma interface chamada TemplateLoader.
    Nos seus testes, vc usa a classe FixtureFactoryLoader para carregar seus templates, vc passa para ela o caminho dos seus pacotes onde estao definidas suas template loaders.

    Vou atualizar o README do projeto com essa documentacao e aviso voces aqui.

  10. Flávio Roberto Cruz Silva 16/10/2013 at 01:37 #

    Na Agile Brazil desse ano quando conversava com o Aniche, ele me falou sobre o FF, desde então o uso nos projetos com uso padrão TestDataBuilder por meio de interfaces fluentes e tenho tido bons resultados e entendimento e manutenibilidade dos testes, pois como sempre digo, cuide com carinho de seus testes, pois teste é código, e código QUEBRA!

  11. Rodrigo Rodrigues 24/12/2013 at 12:07 #

    Oá, muito bom o framework, estou usando e o código de teste está incrivelmente mais eficiente e extremamente limpo, no caso eu criei uns templates para cada entidade onde no local do teste uso algo como:

    Fixture.of(Customer.class).addTemplate(“validCustomer”, new CustomerTemplate());

    List customers = Fixture.from(Customer.class).gimme(10000,
    “valid”);

    Agora uma dúvida, aqui seria para o relacionamento OneToOne certo?

    add(“endereco”, fixture(Endereco.class, “valido”));

    que na versão atual, o fixture está “deprecated” e a gente usa no lugar o método one, teria como da suporte a OneToMany?

    Nesse caso, um cliente tem vários endereços

    Parabéns pelo framework

  12. Douglas Hiura Longo 20/02/2014 at 10:52 #

    Cliente cliente = Fixture.from(Cliente.class).gimme(“valido”);

    Quais as vantagens? se posso fazer:

    Cliente cliente = new Cliente(0,”Douglas”,…);
    OU
    Cliente cliente = new setups.ClienteValido();

    Utiliando tdd? random(a to z) faz sentido?

  13. Anderson Parra 10/03/2014 at 22:34 #

    Oi Rodrigo. Desculpe a demora para responder:

    Deprecamos a utilização de fixture(MyClass.class, “label”). Utilize one(MyClass.class, “label”) e também é possível solicitar uma lista para relacionamentos OneToMany: has(n).of(MyClass.class, “label”)

    Veja o exemplo abaixo:

    Fixture.of(Order.class).addTemplate(“valid”, new Rule(){{
    add(“id”, random(Long.class, range(1L, 200L)));
    add(“items”, has(3).of(Item.class, “valid”));
    add(“payment”, one(Payment.class, “valid”));
    }});

  14. Anderson Parra 10/03/2014 at 23:17 #

    Oi Douglas.

    Códigos como o do seu exemplo: new Cliente(0,”Douglas”,…) nos motivou na criação do f-f. Observamos alguns pontos:

    * Vício na nossa massa de dados, ou seja, se eu mudasse o dado o teste quebrava. Uma das grandes vantagens que o f-f proporciona é a geração de objetos baseados em especificação de limites (templates) e não dado hardcoded. Dessa forma em cada execução dos testes, os objetos de input mudam.
    * Organização dos dados de testes. Você vai utilizar Cliente em muitos testes do seu sistema, por mais clean que nós tentássemos ser, um simples descuido e lá estava o mock de Cliente duplicado (goodbye DRY).
    * Fora outros motivos: testes que venciam, dados em XML, etc.

    Criamos uma API para facilitar a criação / organização / reutilização dos objetos e acrescentamos alguns temperos conforme surgiram as necessidades 🙂

    Você pode optar por tentar organizar seus códigos de testes de outra maneira como você sugeriu: new setups.ClienteValido();
    ou usar o f-f: Fixture.from(Client.class).gimme(“valid”); A preocupação com uma bateria clean é sempre válida.

    Sua última pergunta é a mais difícil: Faz sentido utilizar dado randômico em TDD? Depende. rs (1) É estranho no inicio, você fica sem saber do que fazer assert. A grande sacada é abandonar a dependência do dado, focar no comportamento do método e fazer assert dos limites. O resultado vai ser um teste de comportamento sem vício de dados. (2) Às vezes não tem como fugir de um mock hardcoded. O f-f permite você fazer isso e com a possibilidade de múltiplas labels para a mesma classe, você pode utilizar o …gimme(“label”) conforme sua necessidade.

  15. Carlos 31/03/2014 at 05:20 #

    Gostei da farramenta ve com um colega e vo aprofundar para poder usar

  16. Fernando Vitor Santos Vasconcellos 19/05/2014 at 10:40 #

    Fala Anderson,

    Tem algum jeito pelo framework de popular uma lista com objetos de templates diferentes?

    Por exemplo, no código abaixo eu atribuo 3 objetos da classe Item formados a partir do template “valid” ao atributo Items:

    add(“items”, has(3).of(Item.class, “valid”));

    Teria alguma forma simples de atribuir 3 objetos vindos de templates diferentes a essa lista ou terei que montar minha lista a parte para só depois atribuir?

  17. Maurício Aniche 19/05/2014 at 15:27 #

    Oi Fernando,

    A melhor maneira para tirar dúvidas sobre o framework é na lista de discussão do próprio projeto!

    Um abraço!

  18. Rodrigo 10/09/2014 at 15:06 #

    Voltando aqui mais uma vez, só para dizer que estou usando nos meus testes, simplificou muito, muito mesmo!

    Parabéns pela iniciativa!

Deixe uma resposta