This is a follow on from Cucumber for beginners: Getting Started. If you're looking for cucumber basics, check that out first.
What are factories?
More often than not, you’ll need some data in your database when running a test. Factory Girl is a Rails gem from the always excellent ThoughtBot, which makes setting up and working with test data as easy as you like.
Factory Girl is geared around the idea of 'Factories'. These factories are ways of quick creating instances of a Rails model within a test. Factories live in the
spec/factories.rb file in the root of your project.
Installation is as easy as adding this to your Gemfile:
Let’s say you are testing a simple blog app which has a
Post model, and you want to be able to quickly create blog posts in your tests. With Factory Girl you can accomplish this easily. The code below defines a post factory, which we can later use to build a post model in our test.
# spec/factories.rb FactoryGirl.define do factory :post do title "Test blog post title" content "This is a test blog post." end end
Having defined your post factory, you can now create a post in a step definition like so:
Given(/^A blog post exists in the database$/) do @post = create(:post) end
create(:post) function creates a post record in the database, with the attributes specified in the factory. It’s important to note that Factory Girl links to ActiveRecord, so your
Post model has to have
But what if you wanted to include a custom attribute when you create the model (for example a user id)? Factory Girl makes that easy too:
@post = create(:post, :user_id => 1) # @post is created in the database and has a user_id of 1
As we've seen, Using the
create function will actually create a record in the database. However, if you only wanted to build a record without adding it to the database (for example if you wanted to test a signup form), you can use the
Given(/^I fill in the new post form$/) do @post = build(:post) fill_in "post[title]", :with => @post.title fill_in "post[content]", :with => @post.content click_button("Submit") end
In the above example the Post has not been added to the database (because we're using the
build function), so we can just use the
@post object to fill in the form values, then create the record by clicking the Submit button as we would do on a live site. This is useful for testing form logic.
Also worth mentioning here is that objects you create in your tests are accessible across all steps in a given scenario:
Given(/^there is a new post in the database$/) do @post = create(:post) end Then(/^that post should be visible on the home page $/) do # @post is accessible because it was instantiated in the previous step page.has_content(@post.title).should == true end
A given type of data will often have more than one variant you might want to use. For example, you might need to test both a published and unpublished version of a blog post, or a confirmed and unconfirmed member. Factory Girl makes it easy to extend existing data types to make new variations. Let's say you wanted to make an open and closed version of the
post factory above:
factory :post do title "Test blog post title" content "This is a test blog post." status "open" factory :closed_post do status "closed" end end
You could then create a closed post which would inherit all the attributes of the post, but have a status of closed:
Given(/^there is a closed post in the database$/) do @closed_post = create(:closed_post) end
More info on inheritance can be found on the Factory Girl docs.
Generating random data with Faker
Often you'll need random names, emails and passwords when creating factories. Doing this manually can be tedious at best. Enter Faker, an easy way to generate random data. Install by adding this to your Gemfile:
Then again running
Faker generates a whole heap of different types of fake data (check out the Usage section in the Readme). Let's say we wanted to define a member factory with some faker data.
factory :member do name Faker::Name.name email Faker::Internet.email password Faker::Internet.password end
When we now call
create(:member) the returned object will be populated with random data from Faker:
Given(/^there is a member in the database$/) do @member = create(:member) puts @member.name #=> "Christophe Bartell" puts @member.email #=> "email@example.com" puts @member.password #=> 197g38ths9s end
These attributes will be different every time, so if you need to generate a lot of records at once you won't run into validation errors. Some methods also accept arguments, so generating a 6 character password becomes as easy as running
Faker::Internet.password(6). Again, look at the Faker docs for more info.