[ zombie storage ]

Embedding objects with Fabrication in Rails 3.1

Sidenote from 2021: original URL for this post was http://zombie-storage.com/?p=79

This post is about Ruby on Rails 3.1 testing with RSpec. It’s a bit of a departure from my usual content but it is my $dayjob and this will be helpful to folk out there struggling along on the intertubes. This post also assume you know a lot of background information about Ruby, Rails, testing and Fabrication.

Fabrication is a great gem. I use it with RSpec and Cucumber because it works with Mongoid and seems less buggy than some of the other object generation frameworks. One issue I’ve had with it is how to create embedded objects which you should be using if you have nested forms. Nested object should definitely be tested, doubly so if you have any special validations running. So here is how to write a Fabricator that actually embeds objects.

These are some example objects for a user who speaks languages. The user.rb model

class User  
    include Mongoid::Document
    field :languages
    embeds_many :languages
    validates_presence_of :languages
    attr_accessible :languages_attributes
    accepts_nested_attributes_for :languages,
        :reject_if => lambda { |a| a[:language].blank? },
        :allow_destroy => true
end

The language.rb model

class Language
    include Mongoid::Document
    field :known_language
    field :proficiency
    key :language
    embedded_in :user
    attr_accessible :language, :proficiency
end

The Fabricators. Remember that Fabricators have a special directory layout. Mine are in spec/fabricators/ then a folder with the same name as the model for each Fabricator.

The language fabricator (spec/fabricator/language/fabricator.rb)

Fabricator(:language) do
    known_language "en-US"
    proficiency "1"
end

The user fabricator (spec/fabricator/user/fabricator.rb)

Fabricator(:user) do
    languages { [ Fabricate.build(:language,
                                  :known_language => "en-US",
                                  :proficiency => "1"),
                  Fabricate.build(:language,
                                  :known_language => "ko-KR",
                                  :proficiency => "1") ] }
end

A dodgy test user_spec.rb file

require 'spec_helper'

describe User do
    describe "a user with two langauges" do
        it "should be allowed to enter the world" do
            two_language_knowing_user = Fabricate.build(:user)
            two_language_knowing_user.should be_valid
        end
        it "should know two langauges" do
            two_language_knowing_user = Fabricate.build(:user)
            two_language_knowing_user.languages.count.should == 2
        end
    end
end

Works like a boss. Jawsome.