Factory Girl - What's the Purpose

Factory Girl - what's the purpose?

Gems like Factory Girl and Sham allow you to create templates for valid and re-usable objects. They were created in response to fixtures which where fixed records that had to be loaded into the database. They allow more customization when you instantiate the objects and they aim to ensure that you have a valid object to work with. They can be used anywhere in your tests and in your before and after test hooks.

before(:each), before(:all), after(:each) and after(:all) all aim to give you a place to do setup and teardown that will be shared amongst a test group. For example, if you are going to be creating a new valid user for every single test, then you'll want to do that in your before(:each) hook. If you are going to be clearing some files out of the filesystem, you want to do that in a before hook. If your tests all create a tmp file and you want to remove it after your test, you'll do that in your after(:each) or after(:all) hook.

The ways these two concepts differ is that Factories are not aimed at creating hooks around your tests, they are aimed at creating valid Ruby objects and records, so that you can keep your object creation flexible and DRY. Before and after hooks are aimed at setup and teardown tasks that are shared in an example group so that you can keep your setup and teardown code DRY.

What's the difference between mock, stub, and factory girl?

You could think of a mock (or double) as a fake object. When you're testing and need to work with an object that isn't easily usable inside your test, you could use a mock as an approximation for how you expect that object to behave and work around it. Stubs can be used in a similar manner but on an individual method on an object.

Here's a rather contrived example of using a lot of both:

class Client
def connect_to_server
if Server.connect.status == 'bad'
show_an_error
else
do_something_else
end
end
def do_something_else; end
def show_an_error; end
end

context "failure" do
it "displays an error" do
bad_network_response = double("A bad response from some service", :status => 'bad')
Server.should_receive(:connect).and_return(bad_network_response)

client = Client.new
client.should_receive(:show_an_error)
client.connect_to_server
end
end

You can imagine that using a lot of mocks or stubbing is a bad idea; this is basically masking out parts of your code in your test but, it's an easy solution for some difficult/rare testing scenarios.

Factory Girl is useful for generating data for tests. You would use factories as recipes for creating instances for your models, you might need to test something involving a lot of test data and this would be a situation where using fixtures won't work and creating complicated objects explicitly can be tedious.

Factory Girl vs. User.create -- what's the difference?

You are correct but gems like factory girl and fabrication allow for significantly less code duplication across clases and within different tests when you need to test large sets of data that require different attribute values due to uniqueness requirements. It mixes in methods to easily "manufacture" many different objects for these types of tests.

Factory Girl -- understanding associations

From my experience you define "association" in FactoryGirl when you need to instantiate associated object while creating other object by factory, and without this association your new object would be invalid.

Let's say you have Company and Worker models, and in your application you have validations which prevent creating Worker with invalid company_id attribute. You can have Company without workers (that's why you shouldn't define association for workers in factory), but you can't have Worker without Company. You then add association in factory to lazy-instantiate Company for every Worker created.

So to summarize - you define association when you have belongs_to in model, and when your association in model also have presence validation.

Dependent attributes with Factory Girl

One of the variants is to use before(:create) block

FactoryGirl.define do
factory :playlist_entry_episode, class: PlaylistEntry do
start_time Faker::Business.credit_card_expiry_date
episode
premiere false
channel_playlist

before(:create) do |entry|
entry.end_time = entry.start_time + entry.episode.duration
end
end
end

Factory Girl - Manipulate Transient Attribute

I was able to find a suitable solution based on the idea presented by Ben. I used a local array variable to store the types of windows that needed to be created. The traits for each window add a type of window to the array in the after(:build) block. Then, in an after(:create) block I actually create the window records.

The very first after(:build) block (the one not in a trait) is important because that resets the window_types array between object creation.

FactoryGirl.define do
window_types = []

factory :house do
floors 3
exterior 'Brick'

after(:build) do
window_types = []
end

after(:create) do |house|
window_types.each do |window_type|
FactoryGirl.create(:window, window_type, house: house)
end
end

trait :with_picture_window do
after(:build) do
window_types << :picture
end
end

trait :with_double_hung_window do
after(:build) do
window_types << :double_hung
end
end

trait :with_bay_window do
after(:build) do
window_types << :bay
end
end

factory :house_with_bay_and_picture_window, traits: [:bay, :picture]
end
end

I'm very curious to hear any thoughts on this approach. For now, this suits my needs and is very flexible.

Factory Girl and lazy initialization while running db:migrate

ActiveRecord allows :class_name => "ClassNameInQuotes" in its associations, and FactoryGirl can do this too. Just put the class name in quotes.

FactoryGirl.define do
factory :member_data, :class => "Member" do
first_name 'fn_member'
last_name 'ln_member'
end
end


Related Topics



Leave a reply



Submit