Com o conceito de Extreme Programming (XP), publicado em 1999 por Kent Beck, surgiu também o conceito de Test Driven Development (TDD), que propôs a elaboração de códigos de testes antes da implementação efetiva da aplicação.
Os testes sendo implementados primeiro são então seguidos pela implementação da funcionalidade que os faça passar. Com isso, desde seu início até a implementação de suas últimas funcionalidades, o software estará o máximo possível coberto por testes que garantam sua estabilidade e confiabilidade. Uma grande vantagem é a possibilidade de sempre poder realizar refatorações no código, bastando garantir que os testes continuem passando, mantendo assim um código limpo e de alta qualidade e manutenibilidade.
Mas o objetivo aqui é falar da FactoryGirl, uma RubyGem criada pela thoughtbot que simplifica o desenvolvimento de TDD em Rails, sendo uma alternativa ao uso de fixtures.
FactoryGirl
A framework Rails já oferece, por padrão, uma estrutura pronta para a implementação de testes automatizados, com uma proposta nativa de XP, porém, com o passar do tempo, a comunidade passou a oferecer métodos de testes mais sofisticados que vieram a ser utilizados em substituição às opções padrão do Rails na grande maioria dos projetos atuais que vemos por aí.
O espaço então tem sido conquistado por ferramentas como RSpec, Cucumber, Capybara e a FactoryGirl. Essa última se propõe a oferecer uma fábrica de instâncias mais versátil que as fixtures, e cuja lógica fique isolada da implementação específica dos testes. Não se limita a aplicações em Rails, mas é esse cenário que vamos explorar aqui.
Para ilustrar, vamos imaginar o teste das funcionalidades de uma entidade Capitulo. É preciso instanciar um Capitulo cumprindo seus atributos obrigatórios, o que leva à instância de um Livro, que leva à instância de um Autor, que leva à instância de uma Editora, e por aí em diante.
Também em um teste de paginação ou performance em que se necessita criar várias instâncias de determinada entidade, o algoritmo necessário poluiria mais ainda os testes.
Vamos aos exemplos...
Testes com RSpec sem FactoryGirl
No exemplo abaixo, temos um teste com RSpec testando um modelo de Matricula. Para realizar o teste, precisamos instanciar uma Matricula informando todos os seus atributos obrigatórios, o que inclui um Aluno e uma Disciplina, que também necessitarão serem instanciados. Só então conseguiremos testar se a Matrícula é válida quando possui todos os atributos obrigatórios e se é inválida quando algum deles está faltando.
require 'spec_helper' describe Matricula do let(:aluno) { Aluno.create(nome: "Aluno qualquer") } let(:disciplina) { Disciplina.create(nome: "Cálculo") } let(:matricula) { Matricula.create(aluno: aluno, disciplina: disciplina) } subject { matricula } it { should respond_to(:aluno) } it { should respond_to(:aluno_id) } it { should respond_to(:disciplina) } it { should respond_to(:disciplina_id) } it { should be_valid } describe "when aluno_id is not present" do before { matricula.aluno_id = nil } it { should_not be_valid } end describe "when disciplina_id is not present" do before { matricula.disciplina_id = nil } it { should_not be_valid } end ... ... ... end
Instalando FactoryGirl em uma aplicação Rails
Antes de mostrar como a FactoryGirl pode simplificar o exemplo acima, vou mostrar como adicioná-la ao projeto.
Adicione a Gem no gemfile dentro do grupo test, conforme o exemplo abaixo:
... ... group :test do ... gem "factory_girl_rails", "~> 4.0" end ... ...
Execute o comando bundle e a saída no console deve indicar que a gem factory_girl_rails foi instalada com sucesso, bem como sua dependência factory_girl.
Instalação concluída!
Refazendo o teste com FactoryGirl
O primeiro passo é definir o lugar onde as factories vão ser configuradas. Há quatro opções:
test/factories.rb spec/factories.rb test/factories/*.rb spec/factories/*.rb
A opção mais comum é criar um arquivo de factories em spec/factories.rb. Eu pessoalmente já utilizei spec/factories/*.rb em casos que envolviam muitas factories e achei melhor organizar assim.
No arquivo criado, as factories do nosso exemplo são definidas dessa forma:
FactoryGirl.define do factory :aluno do nome "Aluno qualquer" end factory :disciplina do nome "Cálculo" end factory :matricula do aluno disciplina end end
E nosso teste pode ser reescrito da seguinte forma:
require 'spec_helper' describe Matricula do let(:matricula) { FactoryGirl.create(:matricula) } subject { matricula } ... ... ... end
O método create é utilizado para criar e salvar uma instância, adequado para nosso exemplo por necessitarmos do id para verificar se o atributo é nulo. O método build é utilizado para somente instanciar o objeto sem salvá-lo.
Bem mais limpo, especialmente se os modelos de Matricula, Aluno e Disciplina tivessem vários outros atributos! Além de que é possível reaproveitar as factories em quantos testes forem necessários.
Associações
A atribuição de associações merece uma explicação. Quando o nome da entidade e o nome do atributo se correspondem, basta indicar esse nome e a associação é automaticamente identificada pela FactoryGirl, como no nosso exemplo. Se algo foge ao padrão, basta especificar com a diretiva association, conforme exemplo abaixo:
... factory :livro do titulo "The RSpec Book" association :autor, factory: :pessoa, nome: "David Chelimsky" end ...
Sequences
A demonstração de como a FactoryGirl pode simplificar os testes já foi feita. Agora vou abordar algumas outras funcionalidades interessantes oferecidas, como o recurso de sequences. Este recurso é essencial quando há validações de unicidade em atributos de modelos. A criação de várias instâncias através das factories não passaria pelas validações e acabaria por não conseguir persistir as instâncias. As sequences atribuem um número incrementalmente, de forma a sempre criar um valor diferente:
... factory :livro do sequence :titulo { |n| "Livro #{n}" } ... end ...
Várias chamadas a FactoryGirl.create(:livro) criariam livros com nomes "Livro 1", "Livro 2", "Livro 3" e assim por diante.
Herança
O recurso de herança está disponível para quando se deseja criar mais de uma factory para um mesmo modelo, sendo uma mais especializada que outra. Vamos utilizar um exemplo de uma factory que cria Livros. Os livros "fabricados" são, por padrão, novos. Mas pode haver situações em que se deseja testar livros usados. O exemplo abaixo demonstra como implementar tal tipo de situação:
FactoryGirl.define do factory :livro do titulo "The RSpec Book" association :autor, factory: :pessoa, nome: "David Chelimsky" novo true factory :livro_usado do novo false end end end
Em qualquer lugar nos testes poderia ser chamada a Factory para cada um dos tipos de livro:
... let(:livro) { FactoryGirl.create(:livro) } let(:livro_usado) { FactoryGirl.create(:livro_usado) } ...
Quando os modelos estão organizados em namespaces
Conforme demonstrado em um post anterior sobre roteamento de resources em Rails, é possível criar namespaces para organizar melhor os modelos e controladores da aplicação quando a quantidade de funcionalidades fica grande demais. Nesse caso, o uso de FactoryGirl se depara com uma situação nova, pois o nome do modelo a ser fabricado é inferido pelo nome da factory, mas nessa situação o nome da factory não vai conseguir encontrar o modelo correto.
Nessa situação, para o modelo abaixo, seria necessária uma diretiva adicional no mapeamento da factory indicando a classe exata conforme o exemplo abaixo:
class Comercial::Produto ... ... end
FactoryGirl.define do factory(:produto, :class => Comercial::Produto) do ... ... end end
Atalho para comandos
Outra possibilidade bastante interessante é remover a necessidade de prefixar todas as chamadas com "FactoryGirl..." adicionando os seus comandos por padrão em todos os testes RSpec. Para tanto, é necessário adicionar a seguinte linha no seu arquivo spec/spec_helper.rb.
... ... RSpec.configure do |config| ... ... config.include FactoryGirl::Syntax::Methods end
E podendo assim chamar as factories diretamente:
... let(:livro) { create(:livro) } let(:livro_usado) { create(:livro_usado) } ...
Além
Nos meus projetos atuais, essas foram as funcionalidades de maior utilidade para mim. Há, no entanto, muitas outras possibilidades para serem exploradas envolvendo não somente FactoryGirl, mas TDD em geral. Por isso recomendo algumas referências que utilizei como a página Getting Started oficial, os episódios #158 e #275 do Railscasts e o livro The RSpec Book.
Por último, mas não menos importante, o livro Rails Test Prescriptions, no capítulo 6, trás uma abordagem perfeita sobre a vantagem de Factories sobre Fixtures e os aspectos que desfavorecem a utilização dessas últimas.
Nenhum comentário:
Postar um comentário