Writing on the principles & practice of building digital products

Cucumber: Test data

When testing your Rails apps with Cucumber, you need data to test with. This is a short introduction on how to create that data with the Factory Girl and Faker gems.

This is a follow on from Cucumber for beginners: Getting Started. If you're looking for cucumber basics, check that out first.

Contents

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:

gem 'factory_girl'

Then running bundle install.

Creating factories

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

The 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 title and content attributes.

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 build function:

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 

Inheriting factories

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:

gem 'faker'

Then again running bundle install.

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 #=> "kirsten.greenholt@corkeryfisher.info"
  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.

Resources