[Unit Testing for Zombie] 06. Using Factory
FACTORIES
Convert the zombies fixture to a Factory Girl Factory called :zombie.
test/fixture/zombies.yml
zombie: name: 'Sally' graveyard: 'Valley Dim'
Answer:
test/factories/zombies.rb
FactoryGirl.define do factory :zombie do name 'Sally' graveyard 'Valley Dim' end end
Why using factory instead of fixture?
COMPLEX FACTORIES
Use a nested Factory Girl definition to create zombie factories named :sally and :moe, using the data from the fixtures below.
test/fixtures/zombies.yml
ash: name: 'Ash' graveyard: 'Petrosville' sally: name: 'Sally' graveyard: 'Valley Dim' moe: name: 'Moe' graveyard: 'Petrosville'
Answer:
test/factories/zombies.rb
FactoryGirl.define do factory :zombie do name 'Ash' graveyard 'Petrosville' # Add sally and moe here factory :sally do name 'Sally' graveyard 'Valley Dim' end factory :moe do name 'Moe' end end end
How to create a factory?
What's good in factory?
We can use nested factory to create new factory and reused common part.
UNIQUE ATTRIBUTES
Refactor the zombie factory using a sequence so that we get a unique name and graveyardeverytime we create a new zombie.
FactoryGirl.define do factory :zombie do sequence(:name) {|i| "Ash#{i}" } sequence(:graveyard) { |j| "Petrosville Cemetary#{j}" } end end
Why using sequence?
Every time using a Factory, it equals to :
FactoryGirl.create(:zombie) //create new zombie instance and save into db
But if the data should be uniqueness, then it will cause some problem like: ActiveRecord: RecordInvalid.
Then we can use sequence to solve this problem.
ASSOCIATIONS
Create a tweet factory with a zombie association, don't forget to set a tweet status.
FactoryGirl.define do factory :zombie do name 'Sally' graveyard 'Valley Dim' end end
Answer:
FactoryGirl.define do factory :tweet do status "Eat a brain" association :zombie end end
What about data relationship?
USING FACTORIES
Using factories write a test to verify that a tweet is invalid without a status. Be sure tobuild the Tweet, rather than create it.
FactoryGirl.define do factory :tweet do association :zombie status "Need brain factory." end end
Answer:
class TweetTest < ActiveSupport::TestCase test "A tweet requires a status" do tweet = Factory.build(:tweet, status: nil) assert !tweet.valid?, "Status is not being validated" end end
USING FACTORIES II
Create a tweet using a factory. Then, using Capybara, go to the tweets_url, click on thetweet.status link. Finally, assert that the tweet's show page contains the@tweet.zombie.name in its h3. Use Capybara's within and has_content? methods.
//index.html <ul class="tweets"> <li><%= link_to @tweet.status, tweets_url(@tweet) %></li> </ul>
//show.html <div id='<%="tweet_#{@tweet.id}"%>'> <h3><%= @tweet.zombie.name %></h3> <p><%= @tweet.status %></p> </div>
test/factories/tweets.rb
FactoryGirl.define do factory :tweet do association :zombie status "Need brain factory." end end
factories/zombies.rb
FactoryGirl.define do factory :zombie do name "Ash" graveyard "Factory Hills Cemetary" end end
Answer:
class TweetTest < ActionDispatch::IntegrationTest test "tweet page has zombie link" do tweet = Factory(:tweet) visit tweets_url click_link tweet.status within("h3") do assert has_content? (tweet.zombie.name) end end end