Fail messages for AssertioNs boolean_test.rb ... 4 tests, 4 assertions, 0 failures, 0
errors, 0 skips ... rails generate scaffold zombie name:string graveyard:string.
Test UNit - Level 1 -
TestiNg Philosophy Strict Test Driven Design Test First Verification Testing No Testing
TEST UNIT
what is UNit TestiNg? Test individual parts in isolation Easy to debug Documentation
TEST UNIT
Why Test::UNit? ★ Rails
uses Test::Unit
★ Similar
to other testing libraries
t o b l a T l e i n a h t a N TEST UNIT
Basic Test::UNIt File Structure _test.rb
Included with Ruby
require "test/unit" class Test < Test::Unit::TestCase def test_ end end
Typically one assertion per test TEST UNIT
A passiNg BooleaN Test boolean_test.rb require "test/unit" class BooleanTest < Test::Unit::TestCase def test_true_is_true assert true end end $ ruby boolean_test.rb Loaded suite boolean_test Started . Finished in 0.000389 seconds.
Passed!
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
TEST UNIT
A Failing BooleaN Test boolean_test.rb class BooleanTest < Test::Unit::TestCase def test_true_is_true assert false end end $ ruby boolean_test.rb Loaded suite boolean_test Started F Finished in 0.000550 seconds.
Failed!
1) Failure: test_true_is_true(BooleanTest) [boolean_test.rb:5]: Failed assertion, no message given.
Not Descriptive
1 tests, 1 assertions, 1 failures, 0 errors, 0 skips
TEST UNIT
Fail messages for AssertioNs boolean_test.rb class BooleanTest < Test::Unit::TestCase def test_true_is_true assert false, "True should be truthy" end end $ ruby boolean_test.rb Loaded suite boolean_test Started F Finished in 0.000550 seconds.
Descriptive!
1) Failure: test_true_is_true(BooleanTest) [boolean_test.rb:5]: True should be truthy 1 tests, 1 assertions, 1 failures, 0 errors, 0 skips
TEST UNIT
Test First Example string_extension_test.rb class StringExtensionTest < Test::Unit::TestCase def test_is_number assert "3".is_number? end def test_is_not_number assert !"Blah".is_number? end end
TEST UNIT
RuN the Tests $ ruby string_extension_test.rb Loaded suite string_test Started EE Finished in 0.000817 seconds. 1) Error: test_is_not_number(StringExtensionTest): NoMethodError: undefined method `is_number?' for "Blah":String string_test.rb:39:in `test_is_not_number' 2) Error: test_is_number(StringExtensionTest): NoMethodError: undefined method `is_number?' for "3":String string_test.rb:33:in `test_is_number' 2 tests, 0 assertions, 0 failures, 2 errors, 0 skips
TEST UNIT
Write the Code to make tests pass string_extension.rb class String def is_number? if self =~ /^\d+$/ true else false end end end
string_extension_test.rb assert "3".is_number? assert !"Blah".is_number?
TEST UNIT
RuN the Tests string_extension_test.rb require "test/unit" require "string_extension" ...
Specify Load Path
$ ruby -I. string_extension_test.rb Loaded suite string_extension_test Started .. Finished in 0.000624 seconds.
2 tests, 2 assertions, 0 failures, 0 errors, 0 skips
TEST UNIT
string_extension.rb class String def is_number? if self =~ /^\d+$/ true else false end end end
string_extension_test.rb assert "3".is_number? assert !"Blah".is_number?
TEST UNIT
string_extension.rb class String def is_number? self =~ /^\d+$/ end end
then rerun tests!
Red, GreeN, Refactor $ ruby -I. string_extension_test.rb Loaded suite string_extension_test Started .. Finished in 0.000624 seconds. 2 tests, 2 assertions, 0 failures, 0 errors, 0 skips
RED GREEN REFACTOR TEST UNIT
Let’s add a humaNize Method ★
Should lowercase and capitalize
string_extension_test.rb def test_humanize_function_added_to_string assert_respond_to "blah", :humanize end
string_extension.rb class String def humanize end end
object method
3 tests, 3 assertions, 0 failures, 0 errors, 0 skips
TEST UNIT
assert_Not_Nil string_extension_test.rb def test_humanize_returns_something assert_not_nil "Yo".humanize, "humanize is returning nil" end
string_extension.rb class String def humanize "Yo" end end
optional error message
4 tests, 4 assertions, 0 failures, 0 errors, 0 skips
TEST UNIT
assert_equal string_extension_test.rb def test_humanize assert_equal "Likes me brains!", "LIKES ME BRAINS!".humanize end
expected
string_extension.rb
actual
class String def humanize self.downcase.capitalize end end 5 tests, 5 assertions, 0 failures, 0 errors, 0 skips
TEST UNIT
assert_Match string_extension_test.rb def test_just_for_brains assert_match /brains/, "LIKES ME BRAINS!".humanize end
regex
string_extension.rb
string
class String def humanize self.downcase.capitalize end end 6 tests, 6 assertions, 0 failures, 0 errors, 0 skips
TEST UNIT
assert_raise string_extension_test.rb def test_zombie_in_humanize_raises_error assert_raise(RuntimeError) { "zombie".humanize } end
string_extension.rb class String def humanize if self =~ /zombie/ raise RuntimeError else self.downcase.capitalize end end end 7 tests, 7 assertions, 0 failures, 0 errors, 0 skips
TEST UNIT
AssertioNs assert assert_equal ,
assert_not_equal
assert_respond_to , : assert_nil
assert_not_nil
assert_match ,
assert_no_match
assert_raise() {
}
+ optional error message
assert_kind_of(, )
TEST UNIT
Model testing - Level 2 -
GeNeratiNg Rails Tests $ rails generate scaffold zombie name:string graveyard:string ... invoke test_unit create test/unit/zombie_test.rb create test/fixtures/zombies.yml
/test/unit/zombie_test.rb require 'test_helper'
test configuration
class ZombieTest < ActiveSupport::TestCase # test "the truth" do # assert true # end end
No Tests
MODEL TESTING
Rails ENviroNmeNts /config/database.yml development: adapter: sqlite3 database: db/development.sqlite3 pool: 5 timeout: 5000
only for testing
test: adapter: sqlite3 database: db/test.sqlite3 pool: 5 timeout: 5000 production: ...
MODEL TESTING
Rails Rake Tasks $ rake db:test:prepare
check for migrations + load schema
$ rake test
run db:test:prepare + run all tests
$ rake
To run an individual test $ ruby -Itest test/unit/zombie_test.rb
needed to find test_helper
To run a single test case $ ruby -Itest test/unit/zombie_test.rb -n test_the_truth
MODEL TESTING
Our first model test /test/unit/zombie_test.rb require 'test_helper' class ZombieTest < ActiveSupport::TestCase def test_invalid_without_a_name z = Zombie.new assert !z.valid?, "Name is not being validated" end end
MODEL TESTING
Improved test defiNitioN /test/unit/zombie_test.rb require 'test_helper' class ZombieTest < ActiveSupport::TestCase test "invalid without a name" do z = Zombie.new assert !z.valid?, "Name is not being validated" end end $ rake You have 1 pending migrations: 20120114152945 CreateZombies Run "rake db:migrate" to update your database then try again.
Whoops need to migrate!
MODEL TESTING
model test iN the Red run migrations
$ rake db:migrate == CreateZombies: migrating ================================================== -- create_table(:zombies) -> 0.0012s == CreateZombies: migrated (0.0013s) =========================================
$ rake 1) Failure: test_invalid_without_a_name(ZombieTest) [test/unit/zombie_test.rb:6]: Name is not being validated 1 tests, 1 assertions, 1 failures, 0 errors, 0 skips
MODEL TESTING
TurNiNg it GreeN /test/unit/zombie_test.rb require 'test_helper' class ZombieTest < ActiveSupport::TestCase test "invalid without a name" do z = Zombie.new assert !z.valid?, "Name is not being validated" end end
/app/models/zombie.rb class Zombie < ActiveRecord::Base validates :name, presence: true end 1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
MODEL TESTING
Our secoNd Validity Test /test/unit/zombie_test.rb require 'test_helper' class ZombieTest < ActiveSupport::TestCase test "valid with all attributes" do z = Zombie.new z.name = 'Ash' z.graveyard = 'Oak Park' assert z.valid?, 'Zombie was not valid' end end
Needs Fixture MODEL TESTING
Fixtures /test/fixtures/zombies.yml one: name: MyString graveyard: MyString
ash: name: Ash graveyard: Oak Park
two: name: MyString graveyard: MyString
tim: name: Tim graveyard: Green Meadows
z = Zombie.new z.name = 'Ash' z.graveyard = 'Oak Park'
MODEL TESTING
pulls model from db z = zombies(:ash)
Loaded into database on start + transactional
Test with Fixture /test/unit/zombie_test.rb require 'test_helper' class ZombieTest < ActiveSupport::TestCase test "valid with all attributes" do z = zombies(:ash) assert z.valid?, "Zombie was not valid" end test "invalid name gives error message" do z = zombies(:ash) z.name = nil z.valid? assert_match /can't be blank/, z.errors[:name].join, "Presence error not found on zombie" end end
MODEL TESTING
New Method /test/unit/zombie_test.rb test "can generate avatar_url" do z = zombies(:ash) assert_equal "http://zombitar.com/#{z.id}.jpg", z.avatar_url end
/app/models/zombie.rb class Zombie < ActiveRecord::Base validates :name, presence: true def avatar_url "http://zombitar.com/#{id}.jpg" end end
MODEL TESTING
TestiNg a RelatioNship /test/unit/zombie_test.rb test "should respond to tweets" do z = zombies(:ash) assert_respond_to z, :tweets end test "should contain tweets" do z = zombies(:ash) assert_equal z.tweets, [?, ?] end
Need more fixtures MODEL TESTING
More Fixtures /test/fixtures/zombies.yml ash: id: 1 name: Ash graveyard: Oak Park
ash_1: status: Eating Eyebrows zombie_id: 1
tim: id: 2 name: Tim graveyard: Green Meadows
zombies(:ash).tweets
MODEL TESTING
/test/fixtures/tweets.yml
same
ash_2: status: Lurking 4 Brains zombie_id: 1
[tweets(:ash_2), tweets(:ash_1)]
TestiNg Zombie Tweets /test/unit/zombie_test.rb test "should contain tweets" do assert_equal [tweets(:ash_2), tweets(:ash_1)], zombies(:ash).tweets end
Brittle! Dependent on fixtures test "should contain only tweets that belong to zombie" do z = zombies(:ash) assert z.tweets.all? {|t| t.zombie == z } end
MODEL TESTING
cleaNiNg it up - Level 3 -
setup method /test/unit/zombie_test.rb test "should contain only tweets that belong to zombie" do z = zombies(:ash) assert z.tweets.all? {|t| t.zombie == z } end test "can generate avatar_url" do z = zombies(:ash) assert_equal "http://zombitar.com/#{z.id}.jpg", z.avatar_url end
Duplication! CLEANING IT UP
setup method /test/unit/zombie_test.rb def setup @z = zombies(:ash) end
Run before every test
test "should contain only tweets that belong to zombie" do assert @z.tweets.all? {|t| t.zombie == @z } end test "can generate avatar_url" do assert_equal "http://zombitar.com/#{ @ z.id}.jpg", @z.avatar_url end
CLEANING IT UP
RefactoriNg our Tests /test/unit/zombie_test.rb test "invalid name gives error message" do @z.name = nil @z.valid? assert_match /can't be blank/, @z.errors[:name].join, "Presence error not found on zombie" end test "invalid graveyard gives error message" do @z.graveyard = nil @z.valid? assert_match /can't be blank/, @z.errors[:graveyard].join, "Presence error not found on zombie" end
Duplication!
CLEANING IT UP
RefactoriNg our Tests /test/unit/zombie_test.rb def assert_presence(model, field) model .valid? assert_match /can't be blank/, , model.errors[field].join, "Presence error for #{field} not found on #{model.class}" end test "invalid name gives error message" do @z.name = nil assert_presence(@z, :name) end test "invalid graveyard gives error message" do @z.graveyard = nil assert_presence(@z, :graveyard) end
CLEANING IT UP
RefactoriNg our Tests /test/test_helper.rb
! t i e s u n a c s t s e t l l a w o N
class ActiveSupport::TestCase ... def assert_presence(model, field) model .valid? assert_match /can't be blank/, , model.errors[field].join, "Presence error for #{field} not found on #{model.class}" end end
/test/unit/zombie_test.rb test "invalid name gives error message" do @z.name = nil assert_presence(@z, :name) end test "invalid graveyard gives error message" do @z.graveyard = nil assert_presence(@z, :graveyard) end
INtroduciNg Shoulda Makes tests easy on the fingers and the eyes Gemfile group :test do gem 'shoulda' end
test "invalid name gives error message" do @z.name = nil assert_presence(@z, :name) end
using shoulda
should validate_presence_of(:name)
CLEANING IT UP
created by
UsiNg Shoulda
/test/unit/zombie_test.rb
class ZombieTest < ActiveSupport::TestCase should validate_presence_of(:name) should validate_presence_of(:graveyard) should ensure_length_of(:name).is_at_most(15) should have_many(:tweets) end
should validate_uniqueness_of(:name) should ensure_length_of(:password).is_at_least(5).is_at_most(20) should validate_numericality_of(:age) should_not allow_value("blah").for(:email) should allow_value("[email protected]").for(:email) should ensure_inclusion_of(:age).in_range(1..100) should_not allow_mass_assignment_of(:password) should belong_to(:zombie) should validate_acceptance_of(:terms_of_service)
Mocks & Stubs - Level 4 -
a bad Test /app/models/zombie.rb class Zombie < ActiveRecord::Base has_one :weapon
dependent on
def decapitate weapon.slice(self, :head) self.status = "dead again" end end
/test/unit/zombie_test.rb test "decapitate should set status to dead again" do @zombie.decapitate assert_equal "dead again", @zombie.status end
MOCKS AND STUBS
slice
We Need a Stub zombie decapitate
weapon def slice(args*) # complex stuff end
W e n e e d to fak /app/models/zombie.rb
class Zombie < ActiveRecord::Base has_one :weapon def decapitate weapon.slice(self, :head) self.status = "dead again" end end
MOCKS AND STUBS
e this call
INtroduciNg Mocha A library for mocking and stubbing Gemfile group :test do gem 'mocha' end
Stub For replacing a method with code that returns a specified result.
Mock A stub with an assertion that the method gets called.
MOCKS AND STUBS
We Need a Stub zombie
weapon
decapitate
def slice(*args) return nil end
/app/models/zombie.rb class Zombie < ActiveRecord::Base has_one :weapon def decapitate weapon.slice(self, :head) self.status = "dead again" end end
MOCKS AND STUBS
@zombie.weapon.stubs(:slice)
We Need a Stub zombie
weapon
decapitate
def slice(*args) return nil end
/test/unit/zombie_test.rb test "decapitate should set status to dead again" do @zombie.weapon.stubs(:slice) @zombie.decapitate assert "dead again", @zombie.status end
We need to test that slice is called MOCKS AND STUBS
TestiNg that slice gets called zombie
weapon
decapitate
def slice(*args) return nil end
/app/models/zombie.rb
@zombie.weapon.expects(:slice)
class Zombie < ActiveRecord::Base has_one :weapon def decapitate weapon.slice(self, :head) self.status = "dead again" end end
MOCKS AND STUBS
+ has an assert
TestiNg that slice gets called zombie
weapon
decapitate
def slice(*args) return nil end
/test/unit/zombie_test.rb test "decapitate should call slice" do @zombie.weapon.expects(:slice) @zombie.decapitate end
MOCKS AND STUBS
ANother example /app/models/zombie.rb class Zombie < ActiveRecord::Base def geolocate Zoogle.graveyard_locator(self.graveyard) end end
/test/unit/zombie_test.rb test "geolocate calls the Zoogle graveyard locator" do Zoogle.expects(:graveyard_locator).with(@zombie.graveyard) @zombie.geolocate end
stubs the method + assertion with correct param MOCKS AND STUBS
AddEd Complexity /app/models/zombie.rb def geolocate loc = Zoogle.graveyard_locator(self.graveyard) "#{loc[:latitude]}, #{loc[:longitude]}" end
/test/unit/zombie_test.rb test "geolocate calls the Zoogle graveyard locator" do Zoogle.expects(:graveyard_locator).with(@zombie.graveyard) .returns({latitude: 2, longitude: 3}) @zombie.geolocate end
stubs the method + assertion with correct param + return value
MOCKS AND STUBS
Need to test the returNed StriNg /app/models/zombie.rb def geolocate loc = Zoogle.graveyard_locator(self.graveyard) "#{loc[:latitude]}, #{loc[:longitude]}" end
/test/unit/zombie_test.rb test "geolocate returns properly formatted lat, long" do Zoogle.stubs(:graveyard_locator).with(@zombie.graveyard) .returns({latitude: 2, longitude: 3}) assert_equal "2, 3", @zombie.geolocate end
MOCKS AND STUBS
TestiNg with Object Stubs /app/models/zombie.rb
n r u t e r d shoul
def geolocate_with_object loc = Zoogle.graveyard_locator(self.graveyard) "#{loc.latitude}, #{loc.longitude}" end
object def latitude return 2 end def longitude return 3 end
/test/unit/zombie_test.rb test "geolocate_with_object properly formatted lat, long" do loc = stub(latitude: 2, longitude: 3) Zoogle.stubs(:graveyard_locator).returns(loc) assert_equal "2, 3", @zombie.geolocate_with_object end
MOCKS AND STUBS
INtegratioN Tests - Level 5 -
INtegratioN TestiNg Tests the full application stack Tests from the outside “Black box” testing Tests don’t invoke a full web server INTEGRATION TESTS
Why Not View & CoNtroller Tests? Views shouldn’t have logic Controllers shouldn’t have logic Integration tests have you covered
INTEGRATION TESTS
Rails INtegratioN comMaNds Low-level requests: get zombies_path post zombies_path, zombie: {name: 'Ash'} put zombie_path(zombie), zombie: {name: 'Bill'} delete zombie_path(zombie)
follow_redirect!
Exception raised for non-redirect responses INTEGRATION TESTS
Rails INtegratioN AssertioNs :success :redirect :missing :error
assert_response :success assert_response 200
Or any HTTP status symbol used by Rack assert_response :accepted
HTTP 202
assert_response :moved_permanently assert_response :service_unavailable
INTEGRATION TESTS
HTTP 301 HTTP 503
# # # #
HTTP HTTP HTTP HTTP
200 3XX 404 5XX
Rails INtegratioN AssertioNs assert_response :success assert_redirected_to root_url
assert_tag "a", attributes: {href: root_url} assert_no_tag "div", attributes: {id: 'zombie'} assert_select "h1", "Twitter for Zombies"
INTEGRATION TESTS
GeNeratiNg Rails Tests $ rails generate integration_test zombies invoke create
test_unit test/integration/zombies_test.rb
/test/integration/zombies_test.rb require 'test_helper' class ZombiesTest < ActionDispatch::IntegrationTest fixtures :all # test "the truth" do # assert true # end end
INTEGRATION TESTS
No Tests
TestiNg zombie show Page Twitter for Zombies Ash
INTEGRATION TESTS
TestiNg zombie show Page Twitter for Zombies Ash
INTEGRATION TESTS
TestiNg zombie show Page /test/integration/zombies_test.rb require 'test_helper' class ZombiesTest < ActionDispatch::IntegrationTest test "Anyone can view zombie information" do zombie = zombies(:ash) get zombie_url(zombie) assert_response :success assert_select "h1", zombie.name end end
INTEGRATION TESTS
Need something better? Navigate multiple pages Exercise forms and buttons Act more like a web browser
INTEGRATION TESTS
Capybara Simulates user activity Jonas Nickl
visit login_path
look in
Redirects are automatically followed
INTEGRATION TESTS
Capybara Setup add to /Gemfile group :test do gem 'capybara' end
add to /test/test_helper.rb require 'capybara/rails' class ActionDispatch::IntegrationTest include Capybara::DSL def teardown Capybara.reset_sessions! Capybara.use_default_driver end end
INTEGRATION TESTS
CApybara NavigatioN
text or elemen t i d click_link 'Homepage' form label, ele click_button 'Save' ment name, or click_on 'Link or Button text' i fill_in 'First Name', with: 'John' choose 'A Radio Button' check 'A Checkbox' uncheck 'A Checkbox' attach_file 'Image', '/path/to/image.jpg' select 'Option', from: 'Select Box'
current_path current_url
INTEGRATION TESTS
d
Modified Capybara Test /test/integration/zombies_test.rb require 'test_helper' class ZombiesTest < ActionDispatch::IntegrationTest test "Anyone can view zombie information" do zombie = zombies(:ash) visit zombie_url(zombie) assert_equal zombie_path(zombie), current_path within("h1") do assert has_content?(zombie.name) end end end
INTEGRATION TESTS
Capybara Checks has_content? 'Ash'
has_no_content? 'Ash'
within '#zombie_1' do has_content? 'Ash' end
has_selector? '#zombie_1 h1'
has_no_selector? '...'
has_selector? '#zombie_1 h1', text: 'Ash' has_selector? '.zombie', count: 5, visible: true
INTEGRATION TESTS
Capybara Checks has_selector?
DOM elements
has_content?
Textual content
has_link?
Hyperlinks
has_field?
Form fields
has_css?
DOM elements by CSS (default)
has_xpath?
INTEGRATION TESTS
DOM elements by XPath
TestiNg the behavior test "Navigation is available to the zombie page" do zombie = zombies(:ash) tweet = tweets(:hello) visit root_url within("#tweet_#{tweet.id}") do click_link zombie.name assert_equal zombie_path(zombie), current_path end end
INTEGRATION TESTS
TestiNg the behavior test "should create a new zombie" do visit root_url click_link "Sign Up" fill_in "Name", with: 'Breins' fill_in "Graveyard", with: 'BRREEEIIINNNSSS' click_button "Sign Up" assert_equal zombie_tweets_path("Breins"), current_path end
Extract the sign up process into a test helper method def sign_up_as(name, graveyard)
INTEGRATION TESTS
CleaNing the Tests test "should show a welcome message to new signups" do visit root_url sign_up_as 'Breins', 'BRREEEIIINNNSSS' assert has_content?("Welcome Breins"), "Message not displayed" end
INTEGRATION TESTS
UsiNg Factories - Level 6 -
Fixtures are frighteNing Associations are hard ash: id: 1 name: 'Ash' graveyard: 'Oak Park'
ash_hello_world: zombie_id: 1 status: 'Hello World'
Exponentially grow with edge cases Repeat a lot
USING FACTORIES
Factory Girl Implements factory pattern
Define valid model attributes Easily modifiable at runtime Inherit and modify configurations
USING FACTORIES
created by
Factory Girl add to /Gemfile group :development, :test do gem 'factory_girl_rails' end
created by
Load in development to get generator hooks USING FACTORIES
Factory Girl $ rails generate model zombie name:string graveyard:string ... invoke create
factory_girl test/factories/zombies.rb
/test/factories/zombies.rb FactoryGirl.define do factory :zombie do name "MyString" graveyard "MyString" end end
USING FACTORIES
INto the factory /test/fixtures/zombies.yml ash: name: 'Ash' graveyard: 'Oak Park'
zombies(:ash)
USING FACTORIES
/test/factories/zombies.rb FactoryGirl.define do factory :zombie do name 'Ash' graveyard 'Oak Park' end end
Factory(:zombie)
INto the factory Factory(:zombie)
Action to take FactoryGirl.create(:zombie)
FactoryGirl.build(:zombie)
FactoryGirl.attributes_for(:zombie)
USING FACTORIES
FactoryGirl.create(:zombie)
Factory to use
Returns a new, saved Zombie Returns a new, unsaved Zombie Returns a Hash of attributes
INto the factory /test/fixtures/zombies.yml
/test/factories/zombies.rb
ash: name: 'Ash' graveyard: 'Oak Park'
FactoryGirl.define do factory :zombie do name 'Ash' graveyard 'Oak Park'
bill: name: 'Bill' graveyard: 'Oak Park' mike: name: 'Mike' graveyard: 'Sunnyvale'
USING FACTORIES
factory :zombie_bill do name 'Bill' end factory :zombie_mike do name 'Mike' graveyard 'Sunnyvale' end end end
INto the factory /app/models/zombie.rb class Zombie < ActiveRecord::Base validates :name, presence: true, uniqueness: true validates :graveyard, presence: true end
zombie_1 = Factory(:zombie) # zombie_2 = Factory(:zombie) ActiveRecord::RecordInvalid: Validation failed: Name has already been taken
USING FACTORIES
INto the factory zombie_2 = Factory(:zombie)
zombie_2 = Factory(:zombie_bill)
zombie_2 = Factory(:zombie, name: 'Ash1')
FactoryGirl.define do factory :zombie do sequence(:name) { |i| "Ash#{i}" } graveyard 'Oak Park' end end
USING FACTORIES
Use another factory Use a unique name Use a sequence
INto the factory FactoryGirl.define do factory :zombie do sequence(:name) { |i| "Ash#{i}" } graveyard 'Oak Park' end end
zombie_1 = #
USING FACTORIES
A Zombie Factory 1000.times { Factory(:zombie) }
# # # # # # #
USING FACTORIES
Zombies Need weapoNs /app/models/weapon.rb class Weapon < ActiveRecord::Base belongs_to :zombie validates :zombie, presence: true end
/test/factories/weapon.rb FactoryGirl.define do factory :weapon do name 'Broadsword' association :zombie end end
USING FACTORIES
/test/factories/zombie.rb FactoryGirl.define do factory :zombie do ... end end
Zombies Need weapoNs class Zombie < ActiveRecord::Base has_one :weapon end
Class can’t be inferred FactoryGirl.define do factory :armed_zombie, class: Zombie do sequence(:name) { |i| "ArmedAsh#{i}" } association :weapon graveyard 'Oak Park' end end
USING FACTORIES
Zombies Need weapoNs FactoryGirl.define do factory :zombie do sequence(:name) { |i| "Ash#{i}" } graveyard 'Oak Park' factory :armed_zombie do association :weapon end end end
Inherits inferred class from parent zombie USING FACTORIES
Zombies Need weapoNs FactoryGirl.define do factory :zombie do sequence(:name) { |i| "Ash#{i}" } graveyard 'Oak Park' factory :armed_zombie do association :weapon, factory: :hatchet end end end
Override the inferred factory name USING FACTORIES
UsiNg FActories test "decapitate should set status to dead again" do zombie = zombies(:ash) zombie.decapitate assert_equal "dead again", zombie.status end
USING FACTORIES
UsiNg FActories test "decapitate should set status to dead again" do zombie = zombies(:not_dead_again_zombie) zombie.decapitate assert_equal "dead again", zombie.status end
USING FACTORIES
UsiNg FActories test "decapitate should set status to dead again" do zombie = FactoryGirl.build(:zombie) zombie.decapitate assert_equal "dead again", zombie.status end
USING FACTORIES
UsiNg FActories test "decapitate should set status to dead again" do zombie = FactoryGirl.build(:zombie, status: 'dead') zombie.decapitate assert_equal "dead again", zombie.status end
USING FACTORIES
UsiNg FActories test "Navigation is available to the zombie page" do zombie = zombies(:ash) tweet = tweets(:hello) visit root_url within("#tweet_#{tweet.id}") do click_link zombie.name assert_equal zombie_path(zombie), current_path end end
USING FACTORIES
UsiNg FActories test "Navigation is available to the zombie page" do zombie = Factory(:zombie) tweet = Factory(:tweet, zombie: zombie) visit root_url within("#tweet_#{tweet.id}") do click_link zombie.name assert_equal zombie_path(zombie), current_path end end
USING FACTORIES
UsiNg FActories test "Navigation is available to the zombie page" do tweet = Factory(:tweet) zombie = tweet.zombie visit root_url within("#tweet_#{tweet.id}") do click_link zombie.name assert_equal zombie_path(zombie), current_path end end
USING FACTORIES