Nota: Este guest post foi escrito por Elomar França.
Quem usa muito fixtures sabe que elas costumam dar bastante dor de cabeça: a separação entre os dados do teste (nas fixtures) e o teste em si fazem com que você tenha que absorver dois contextos pra entender o que está acontecendo, nomeação das fixtures geralmente não ajuda, e o pior de tudo é que seus testes ficam mais difíceis de manter e quebradiços, e muita coisa pode quebrar quando você altera uma fixture. Fixtures suck, and you were lied to.
Pra substituir fixtures e sanar esses problemas surgiram várias bibliotecas que te ajudam a criar, nos seus testes, modelos ActiveRecord com dados pré-definidos de forma simples e prática.
Entre as várias opções, se destacam Factory Girl e Machinist. Factory Girl é feita por ninguém menos que Thoughtbot. Machinist é mais simples e tem uma sintaxe mais elegante, e é sobre ela que eu vou falar um pouco.
O que é Machinist
É uma gem que te permite criar os objetos que você precisa pros seus testes nos próprios testes, gerando objetos populados com base em blueprints (modelos) que você define. Ao invés de fazer isso:
ou isso:
Você pode fazer assim:
Melhor, não? Machinist gera os dados pros campos que você não liga e constrói as associações, pra que você só se preocupe com os dados que importam pro teste. Você diz como o Machinist vai fazer isso com os blueprints.
Instalando
Instale como uma gem
sudo gem install notahat-machinist --source http://gems.github.com
E no seu environments/test.rb:
config.gem 'notahat-machinist', :lib => 'machinist', :source => 'http://gems.github.com'
Ou como um plugin
./script/plugin install git://github.com/notahat/machinist.git
Crie um arquivo ‘blueprints.rb’ na sua pasta de spec (ou test), onde você vai definir seus blueprints, e requira ele no seu spec_helper.rb (ou test_helper.rb ou env.rb)
require File.join(File.dirname(FILE), 'blueprint')
E no bloco before ou setup de seus testes, adicione:
config.before(:each) { Sham.reset }
Definindo seus blueprints
Blueprints são definidos chamando o método blueprint no seu modelo.
Para valores simples, é só passar como argumento. Para valores que precisam ser calculados, como uma associação, você pode passar um bloco:
author { User.make }
Quando o nome da associação é o mesmo nome do modelo, Machinist gera um modelo automaticamente – o “post” no blueprint de Comment é um atalho pra “post { Post.make }”.
Sham e Faker
Quase sempre precisamos gerar sequências ou dados que não podem ser repetir, como logins e emails. Machinist cuida disso com Sham, uma classe que te permite gerar atributos únicos aleatórios.
Sham.name { (1..10).map { ('a'..'z').to_a.rand } }
Pra gerar um valor, Sham.name. O Sham taí pra garantir que você vai ter sempre a mesma sequência de valores quando seus testes rodarem e você não vai ter valores duplicados.
Sham trabalha muito bem junto com Faker, uma gem que gera dados falsos como endereços, logins, emails.
Sham.name { Faker::Name.name }
Pra usar Sham no seu blueprint você pode fazer
name { Sham.name }
Ou ainda usar só
name
E deixar que o Machinist coloque o Sham.name por você.
Usando
Pra criar o objeto com base no seu blueprint, você pode usar três métodos: make, que cria e salva; make_unsaved, que cria mas não salva; e plan, que gera um hash com os atributos do objeto, útil especialmente em teste de controllers. Você pode sobrescrever os atributos do modelo que vai ser gerado passando um hash pra esses métodos.
Blueprints Nomeados
As vezes você precisa gerar variações de um objeto, como comentários aprovados. Pra isso, você pode nomear blueprints:
Os atributos que não forem especificados no blueprint nomeado vão usar a definição do blueprint padrão. No caso acima, o author_name de approved vai ser “Test name”.
Para saber mais
Aqui acaba essa introdução a Machinist. Links interessantes pra quem se interessar:
















Philipe Farias
29/05/2009 @ 22:34
Muito bom post Elomar/Thiago! O Machinist tem uma sintaxe muito clara e com certeza irei utiliza-lo em breve.
Só uma observação quanto ao Factory Girl, ele é bem versátil quanto a sintaxe você pode utilizar a sintaxe do Machinist ou Object Daddy (muito bom também) com ele se preferir.