SlideShare a Scribd company logo
RSpec & Rails
Bunlong Van – Rubyist/Rails Developer
Mail: bunlong.van@gmail.com
Blog: http://geekhmer.github.io
Cover
- What is Rspec?
- RSpec features
- RSpec in action
- Stubs & Mocks
- Stubs & Mocks using RSpec
What is RSpec ?
- Testing framework for Ruby on Rails.
- Replacement for RoR built-in testing tool.
TestUnit
class Calculator < Test::Unit::TestCase
def test_addition
assert_equal(8, Calculator.new(6, 2).addition)
end
def test_subtraction
assert_same(4, Calculator.new(6, 2).subtraction)
end
end
RSpec
desribe Calculator do
let(:calculator) { Calculator.new(6,2) }
it "should return 8 when adding 6 and 2" do
calculator.addition.should eql(8)
end
it "should return 4 when subtracting 2 from 6" do
calculator.subtraction.should eql(4)
end
end
RSpec basics
describe MyClass do # creates initial scope
before do
@object = MyClass.new
end
describe "#some_method" do # creates new scope
it "should ..." do
@object.should ...
end
context "when authorized" do # creates new scope
before do
@object.authorized = true
end
RSpec basics (Con.)
it "should ..." do
@object.should ...
end
end
context "when not authorized" do # creates new scope
it "should ..." do
@object.should ...
end
end
end
it "should ..." do
pending "Fix some model first" # creates pending test
end
end
Why RSpec?
- Readability
- Tests documentation and failure messages
Readability
describe Campaign do
it "should be visible by default" do
campaign.should be_visible
end
it "should have performances visible by default" do
campaign.performances_visible.should be_true
end
it "should not be published at start" do
campaign.should_not be_published
end
Readability (Con.)
it "should not be ready to publish at start - without performances, etc" do
campaign.should_not be_ready_to_publish
end
describe "#allowed_performance_kinds" do
it "should allow all by default" do
campaign.allowed_performance_kinds.should == Performance.kinds
end
end
end
Tests documentation and failure
messages
When tests output matter
- Built-in profiler
- Test run filters
- Conventions
- Built-in matchers
Built-in profiler
Test run filters
def old_ruby
RUBY_VERSION != "1.9.2"
end
describe TrueClass do
it "should be true for true", :if => old_ruby do
true.should be_true
end
it "should be true for String", :current => true do
"".should be_true
end
Test run filters (Cons.)
it "should be true for Fixnum" do
0.should be_true
end
end
Conventions
Built-in matchers
target.should satisfy {|arg| ...}
target.should_not satisfy {|arg| ...}
target.should equal <value>
target.should not_equal <value>
target.should be_close <value>, <tolerance>
target.should_not be_close <value>, <tolerance>
target.should be <value>
target.should_not be <value>
target.should predicate [optional args]
target.should be_predicate [optional args]
target.should_not predicate [optional args]
target.should_not be_predicate [optional args]
target.should be < 6
target.should be_between(1, 10)
Built-in matchers (Cons.)
target.should match <regex>
target.should_not match <regex>
target.should be_an_instance_of <class>
target.should_not be_an_instance_of <class>
target.should be_a_kind_of <class>
target.should_not be_a_kind_of <class>
target.should respond_to <symbol>
target.should_not respond_to <symbol>
lambda {a_call}.should raise_error
lambda {a_call}.should raise_error(<exception> [, message])
lambda {a_call}.should_not raise_error
lambda {a_call}.should_not raise_error(<exception> [, message])
Built-in matchers (Cons.)
proc.should throw <symbol>
proc.should_not throw <symbol>
target.should include <object>
target.should_not include <object>
target.should have(<number>).things
target.should have_at_least(<number>).things
target.should have_at_most(<number>).things
target.should have(<number>).errors_on(:field)
expect { thing.approve! }.to change(thing, :status)
.from(Status::AWAITING_APPROVAL)
.to(Status::APPROVED)
expect { thing.destroy }.to change(Thing, :count).by(-1)
Problems ?
- Hard to learn at the beginning
- Routing tests could have more detailed failure messages
- Rails upgrade can break tests compatibility
RSpec in action
- Model specs (placed under spec/models director)
- Controller specs (placed under spec/controllers directory)
- Helper specs (placed under spec/helpers directory)
- View specs (placed under spec/views directory)
- Routing specs (placed under spec/routing directory)
Model specs
describe Campaign do
it "should be visible by default" do
campaign.should be_visible
end
it "should have performances visible by default" do
campaign.performances_visible.should be_true
end
it "should not be published at start" do
campaign.should_not be_published
end
Model specs (Cons.)
it "should not be ready to publish at start - without performances, etc" do
campaign.should_not be_ready_to_publish
end
describe "#allowed_performance_kinds" do
it "should allow all by default" do
campaign.allowed_performance_kinds.should == Performance.kinds
end
end
end
Controller specs
describe SessionsController do
render_views
describe "CREATE" do
context "for virtual user" do
before do
stub_find_user(virtual_user)
end
it "should not log into peoplejar" do
post :create, :user => {:email => virtual_user.email,
:password => virtual_user.password }
response.should_not redirect_to(myjar_dashboard_path)
end
end
Controller specs (Cons.)
context "for regular user" do
before do
stub_find_user(active_user)
end
it "should redirect to myjar when login data is correct" do
post :create, :user => {:email => active_user.email,
:password => active_user.password }
response.should redirect_to(myjar_dashboard_path)
end
end
end
end
Helper specs
describe CampaignsHelper do
let(:campaign) { Factory.stub(:campaign) }
let(:file_name) { "meldung.jpg" }
it "should return the same attachment URL as paperclip
if there is no attachment" do
campaign.stub(:featured_image_file_name).and_return(nil)
helper.campaign_attachment_url(campaign, :featured_image).
should eql(campaign.featured_image.url)
end
it "should return the same attachment URL as paperclip if there
is attachment" do
campaign.stub(:featured_image_file_name).and_return(file_name)
Helper specs (Cons.)
helper.campaign_attachment_url(campaign, :featured_image).
should eql(campaign.featured_image.url)
end
end
View specs
# view at views/campaigns/index.html.erb
<%= content_for :actions do %>
<div id="hb_actions" class="browse_arena">
<div id="middle_actions">
<ul class="btn">
<li class="btn_blue"><%= create_performance_link %></li>
</ul>
</div>
</div>
<% end %>
<div id="interest_board_holder">
<%= campaings_wall_template(@campaigns) %>
</div>
View specs (Cons.)
# spec at spec/views/campaigns/index.html.erb_spec.rb
describe "campaigns/index.html.erb" do
let(:campaign) { Factory.stub(:campaign) }
it "displays pagination when there are more than 20 published
campaigns" do
assign(:campaigns, (1..21).map { campaign }.
paginate(:per_page => 2) )
render
rendered.should include("Prev")
rendered.should include("Next")
end
end
Routing specs
describe "home routing", :type => :controller do
it "should route / to Home#index" do
{ :get => "/" }.should route_to(:controller => "home", :action => "index",
:subdomain => false)
end
it "should route / with subdomain to Performances::Performances#index
do
{ :get => "http://kzkgop.test.peoplejar.net" }.
should route_to(:namespace => nil, :controller =>
"performances/performances", :action => "index")
end
end
Routing specs (Cons.)
describe "error routing", :type => :controller do
it "should route not existing route Errors#new" do
{ :get => "/not_existing_route" }.should route_to(:controller => "errors",
:action => "new", :path => "not_existing_route")
end
End
describe "icebreaks routing" do
it "should route /myjar/icebreaks/initiated to
Icebreaks::InitiatedIcebreaks#index" do
{ :get => "/myjar/icebreaks/initiated" }.should
route_to(:controller => "icebreaks/initiated_icebreaks",
:action => "index")
end
end
Routing specs (Cons.)
describe "admin routing" do
it "should route /admin to Admin::Base#index" do
{ :get => "/admin" }.should route_to(:controller => "admin/welcome",
:action => "index")
end
end
Stubs & Mocks
Back to unit test assumptions
- A unit is the smallest testable part of an application
- The goal of unit testing is to isolate each part of the program and show
that the individual parts are correct
- Ideally, each test case is independent from the others
you.should use_stubs!
- Isolate your unit tests from external libraries and dependencies
- Propagate skinny methods which has low responsibility
- Single bug should make only related tests fail
- Speed up tests
PeopleJar is using
Are there any problems ?
- Writing test is more time consuming
- Need to know stubbed library internal implementations
- Need to write an integration test first
Stubs in action
User.stub(:new) # => nil
User.stub(:new).and_return(true)
user_object = User.new
user_object.stub(:save).and_return(true)
User.stub(:new).and_return(user_object)
user_object.stub(:update_attributes).with(:username => "test").
and_return(true)
User.stub(:new).and_return(user_object)
User.any_instance.stub(:save).and_return(true)
# User.active.paginate
User.stub_chain(:active, :paginate).and_return([user_object])
Stubs in action
User.stub(:new) # => nil
User.stub(:new).and_return(true)
user_object = User.new
user_object.stub(:save).and_return(true)
User.stub(:new).and_return(user_object)
user_object.stub(:update_attributes).with(:username => "test").
and_return(true)
User.stub(:new).and_return(user_object)
User.any_instance.stub(:save).and_return(true)
# User.active.paginate
User.stub_chain(:active, :paginate).and_return([user_object])
Stubs in action (Cons.)
user_object.stub(:set_permissions).with(an_instance_of(String),
anything).and_return(true)
user_object.unstub(:set_permissions)
# user_object.set_permissions("admin", true) # => true (will use stubbed
method)
# user_object.set_permissions("admin") # => false (will call real method)
Mocks in action
User.should_receive(:new) # => nil
User.should_receive(:new).and_return(true)
User.should_not_receive(:new)
user_object = User.new
user_object.should_receive(:save).and_return(true)
User.stub(:new).and_return(user_object)
user_object.should_receive(:update_attributes).
with(:username => "test").and_return(true)
User.stub(:new).and_return(user_object)
User.any_instance.should_receive(:save).and_return(true) # !
Mocks in action (Cons.)
user_object.should_receive(:update_attributes).once # default
user_object.should_receive(:update_attributes).twice
user_object.should_receive(:update_attributes).exactly(3).times
user_object.should_receive(:set_permissions).
with(an_instance_of(String), anything)
# user_object.set_permissions("admin", true) # Success
# user_object.set_permissions("admin") # Fail
What's the difference between
Stubs and Mocks
- Mocks are used to define expectations and verify them
- Stubs allows for defining eligible behavior
- Stubs will not cause a test to fail due to unfulfilled expectation
In practice - Stub failure
describe ".to_csv_file" do
it "should generate CSV output" do
User.stub(:active).and_return([user])
User.to_csv_file.should == "#{user.display_name},#{user.email}n"
end
end
In practice - Mock failure
describe "#facebook_uid=" do
it "should build facebook setting instance if not exists when setting uid"
do
user.should_receive(:build_facebook_setting).with(:uid => "123")
user.facebook_uid = "123"
end
end
Question?

More Related Content

PDF
Testing Ruby with Rspec (a beginner's guide)
Vysakh Sreenivasan
 
PDF
Factory Girl
Gabe Evans
 
PDF
RSpec 3: The new, the old, the good
mglrnm
 
PPTX
Rspec presentation
Myo T Kyaw
 
PDF
Automated testing with RSpec
Nascenia IT
 
PDF
RSpec 2 Best practices
Andrea Reginato
 
PDF
Rspec API Documentation
SmartLogic
 
PPTX
RSpec: What, How and Why
Ratan Sebastian
 
Testing Ruby with Rspec (a beginner's guide)
Vysakh Sreenivasan
 
Factory Girl
Gabe Evans
 
RSpec 3: The new, the old, the good
mglrnm
 
Rspec presentation
Myo T Kyaw
 
Automated testing with RSpec
Nascenia IT
 
RSpec 2 Best practices
Andrea Reginato
 
Rspec API Documentation
SmartLogic
 
RSpec: What, How and Why
Ratan Sebastian
 

What's hot (20)

PPTX
Rspec 101
Jason Noble
 
PPTX
RSpec and Rails
Alan Hecht
 
PPTX
Introduction to testing in Rails
benlcollins
 
PPT
TDD, BDD, RSpec
Nascenia IT
 
PPT
Testing Javascript with Jasmine
Tim Tyrrell
 
PPTX
Clean Code
Nascenia IT
 
PDF
Rails is not just Ruby
Marco Otte-Witte
 
PDF
Excellent
Marco Otte-Witte
 
KEY
How To Test Everything
noelrap
 
KEY
Testing JS with Jasmine
Evgeny Gurin
 
PDF
Introduction to unit testing
Artem Shoobovych
 
PDF
TDD with PhpSpec
CiaranMcNulty
 
PPTX
AngularJS Unit Testing
Prince Norin
 
PDF
Painless Javascript Unit Testing
Benjamin Wilson
 
PPTX
Why you should be using the shiny new C# 6.0 features now!
Eric Phan
 
PPTX
2019-08-23 API contract testing with Dredd
Ryan M Harrison
 
PDF
Inversion Of Control
Chad Hietala
 
PDF
Crossing the Bridge: Connecting Rails and your Front-end Framework
Daniel Spector
 
PDF
Introduction to Python decorators
rikbyte
 
Rspec 101
Jason Noble
 
RSpec and Rails
Alan Hecht
 
Introduction to testing in Rails
benlcollins
 
TDD, BDD, RSpec
Nascenia IT
 
Testing Javascript with Jasmine
Tim Tyrrell
 
Clean Code
Nascenia IT
 
Rails is not just Ruby
Marco Otte-Witte
 
Excellent
Marco Otte-Witte
 
How To Test Everything
noelrap
 
Testing JS with Jasmine
Evgeny Gurin
 
Introduction to unit testing
Artem Shoobovych
 
TDD with PhpSpec
CiaranMcNulty
 
AngularJS Unit Testing
Prince Norin
 
Painless Javascript Unit Testing
Benjamin Wilson
 
Why you should be using the shiny new C# 6.0 features now!
Eric Phan
 
2019-08-23 API contract testing with Dredd
Ryan M Harrison
 
Inversion Of Control
Chad Hietala
 
Crossing the Bridge: Connecting Rails and your Front-end Framework
Daniel Spector
 
Introduction to Python decorators
rikbyte
 
Ad

Viewers also liked (20)

PDF
Basic RSpec 2
Triet Le Minh
 
PPTX
Автоматизируем тестирование UI с Ruby, Cucumber и Selenium
SQALab
 
KEY
Railsで春から始めるtdd生活
Yamamoto Kazuhisa
 
ODP
Системное тестирование приложений на Ruby on Rails с применением Rspec и Cap...
lshevtsov
 
PPTX
Swift distributed tracing method and tools v2
zhang hua
 
KEY
Action Controller Overview, Season 1
RORLAB
 
PDF
Control review for iOS
William Price
 
PDF
Let's Learn Ruby - Basic
Eddie Kao
 
PDF
September2011aftma
Jennifer Berkshire
 
PPT
jQuery For Beginners - jQuery Conference 2009
Ralph Whitbeck
 
PDF
BDD style Unit Testing
Wen-Tien Chang
 
PPT
Ruby On Rails: Web-разработка по-другому!
Constantin Kichinsky
 
PDF
Learning jQuery in 30 minutes
Simon Willison
 
PDF
A swift introduction to Swift
Giordano Scalzo
 
PPTX
Web application architecture
Tejaswini Deshpande
 
PPT
Introduction to html
vikasgaur31
 
PDF
Infinum iOS Talks #1 - Swift under the hood: Method Dispatching by Vlaho Poluta
Infinum
 
PPTX
Introduction to Web Architecture
Chamnap Chhorn
 
PDF
jQuery and Rails: Best Friends Forever
stephskardal
 
PDF
Swift Programming Language
Giuseppe Arici
 
Basic RSpec 2
Triet Le Minh
 
Автоматизируем тестирование UI с Ruby, Cucumber и Selenium
SQALab
 
Railsで春から始めるtdd生活
Yamamoto Kazuhisa
 
Системное тестирование приложений на Ruby on Rails с применением Rspec и Cap...
lshevtsov
 
Swift distributed tracing method and tools v2
zhang hua
 
Action Controller Overview, Season 1
RORLAB
 
Control review for iOS
William Price
 
Let's Learn Ruby - Basic
Eddie Kao
 
September2011aftma
Jennifer Berkshire
 
jQuery For Beginners - jQuery Conference 2009
Ralph Whitbeck
 
BDD style Unit Testing
Wen-Tien Chang
 
Ruby On Rails: Web-разработка по-другому!
Constantin Kichinsky
 
Learning jQuery in 30 minutes
Simon Willison
 
A swift introduction to Swift
Giordano Scalzo
 
Web application architecture
Tejaswini Deshpande
 
Introduction to html
vikasgaur31
 
Infinum iOS Talks #1 - Swift under the hood: Method Dispatching by Vlaho Poluta
Infinum
 
Introduction to Web Architecture
Chamnap Chhorn
 
jQuery and Rails: Best Friends Forever
stephskardal
 
Swift Programming Language
Giuseppe Arici
 
Ad

Similar to Ruby on Rails testing with Rspec (20)

PDF
Ruby on rails rspec
Bindesh Vijayan
 
PDF
2011-02-03 LA RubyConf Rails3 TDD Workshop
Wolfram Arnold
 
ZIP
Rspec Tips
lionpeal
 
PDF
Telling Stories With RSpec
rahoulb
 
PDF
RSpec best practice - avoid using before and let
Bruce Li
 
PDF
R spec let there be tests
Jo Cranford
 
PDF
Testing survival Guide
Thilo Utke
 
PPTX
Testing C# and ASP.net using Ruby
Ben Hall
 
PDF
Rspec
Amitai Barnea
 
PDF
Tootle returning to rails
Andrew Grimm
 
PDF
Rspec
Sergey Nartimov
 
PDF
RSpec on Rails Tutorial
Wen-Tien Chang
 
PDF
RSpec Quick Reference
Blazing Cloud
 
PDF
What NOT to test in your project
alexandre freire
 
KEY
Tdd for BT E2E test community
Kerry Buckley
 
PDF
Controller specs
Alexander Miller
 
PDF
Rspec and Capybara Intro Tutorial at RailsConf 2013
Brian Sam-Bodden
 
PDF
Testing Merb
Yehuda Katz
 
PDF
Rethinking Testing
pdejuan
 
Ruby on rails rspec
Bindesh Vijayan
 
2011-02-03 LA RubyConf Rails3 TDD Workshop
Wolfram Arnold
 
Rspec Tips
lionpeal
 
Telling Stories With RSpec
rahoulb
 
RSpec best practice - avoid using before and let
Bruce Li
 
R spec let there be tests
Jo Cranford
 
Testing survival Guide
Thilo Utke
 
Testing C# and ASP.net using Ruby
Ben Hall
 
Tootle returning to rails
Andrew Grimm
 
RSpec on Rails Tutorial
Wen-Tien Chang
 
RSpec Quick Reference
Blazing Cloud
 
What NOT to test in your project
alexandre freire
 
Tdd for BT E2E test community
Kerry Buckley
 
Controller specs
Alexander Miller
 
Rspec and Capybara Intro Tutorial at RailsConf 2013
Brian Sam-Bodden
 
Testing Merb
Yehuda Katz
 
Rethinking Testing
pdejuan
 

More from Bunlong Van (9)

PPT
scrummethodology-151002092252-lva1-app6891
Bunlong Van
 
PPT
Scrum methodology
Bunlong Van
 
PPT
Javascript dom event
Bunlong Van
 
PPT
Javascript Object Oriented Programming
Bunlong Van
 
PPT
Basic Javascript
Bunlong Van
 
PPT
Real time web and mobile application with Erlang & Ruby programming language
Bunlong Van
 
PPT
Why ruby?
Bunlong Van
 
PPT
How to develop app for facebook fan page
Bunlong Van
 
PPT
Why less?
Bunlong Van
 
scrummethodology-151002092252-lva1-app6891
Bunlong Van
 
Scrum methodology
Bunlong Van
 
Javascript dom event
Bunlong Van
 
Javascript Object Oriented Programming
Bunlong Van
 
Basic Javascript
Bunlong Van
 
Real time web and mobile application with Erlang & Ruby programming language
Bunlong Van
 
Why ruby?
Bunlong Van
 
How to develop app for facebook fan page
Bunlong Van
 
Why less?
Bunlong Van
 

Recently uploaded (20)

PPT
Activate_Methodology_Summary presentatio
annapureddyn
 
PDF
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
PDF
10 posting ideas for community engagement with AI prompts
Pankaj Taneja
 
PPTX
Odoo Integration Services by Candidroot Solutions
CandidRoot Solutions Private Limited
 
PPTX
Presentation about Database and Database Administrator
abhishekchauhan86963
 
PPTX
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
PPTX
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
PDF
Applitools Platform Pulse: What's New and What's Coming - July 2025
Applitools
 
PDF
Immersive experiences: what Pharo users do!
ESUG
 
PPTX
Presentation about variables and constant.pptx
safalsingh810
 
PPTX
Role Of Python In Programing Language.pptx
jaykoshti048
 
PDF
ChatPharo: an Open Architecture for Understanding How to Talk Live to LLMs
ESUG
 
PPTX
Maximizing Revenue with Marketo Measure: A Deep Dive into Multi-Touch Attribu...
bbedford2
 
PDF
Salesforce Implementation Services Provider.pdf
VALiNTRY360
 
PDF
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
PDF
On Software Engineers' Productivity - Beyond Misleading Metrics
Romén Rodríguez-Gil
 
PDF
Key Features to Look for in Arizona App Development Services
Net-Craft.com
 
PDF
MiniTool Power Data Recovery Crack New Pre Activated Version Latest 2025
imang66g
 
PDF
What to consider before purchasing Microsoft 365 Business Premium_PDF.pdf
Q-Advise
 
PPTX
ASSIGNMENT_1[1][1][1][1][1] (1) variables.pptx
kr2589474
 
Activate_Methodology_Summary presentatio
annapureddyn
 
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
10 posting ideas for community engagement with AI prompts
Pankaj Taneja
 
Odoo Integration Services by Candidroot Solutions
CandidRoot Solutions Private Limited
 
Presentation about Database and Database Administrator
abhishekchauhan86963
 
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
Applitools Platform Pulse: What's New and What's Coming - July 2025
Applitools
 
Immersive experiences: what Pharo users do!
ESUG
 
Presentation about variables and constant.pptx
safalsingh810
 
Role Of Python In Programing Language.pptx
jaykoshti048
 
ChatPharo: an Open Architecture for Understanding How to Talk Live to LLMs
ESUG
 
Maximizing Revenue with Marketo Measure: A Deep Dive into Multi-Touch Attribu...
bbedford2
 
Salesforce Implementation Services Provider.pdf
VALiNTRY360
 
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
On Software Engineers' Productivity - Beyond Misleading Metrics
Romén Rodríguez-Gil
 
Key Features to Look for in Arizona App Development Services
Net-Craft.com
 
MiniTool Power Data Recovery Crack New Pre Activated Version Latest 2025
imang66g
 
What to consider before purchasing Microsoft 365 Business Premium_PDF.pdf
Q-Advise
 
ASSIGNMENT_1[1][1][1][1][1] (1) variables.pptx
kr2589474
 

Ruby on Rails testing with Rspec

  • 1. RSpec & Rails Bunlong Van – Rubyist/Rails Developer Mail: [email protected] Blog: http://geekhmer.github.io
  • 2. Cover - What is Rspec? - RSpec features - RSpec in action - Stubs & Mocks - Stubs & Mocks using RSpec
  • 3. What is RSpec ? - Testing framework for Ruby on Rails. - Replacement for RoR built-in testing tool.
  • 4. TestUnit class Calculator < Test::Unit::TestCase def test_addition assert_equal(8, Calculator.new(6, 2).addition) end def test_subtraction assert_same(4, Calculator.new(6, 2).subtraction) end end
  • 5. RSpec desribe Calculator do let(:calculator) { Calculator.new(6,2) } it "should return 8 when adding 6 and 2" do calculator.addition.should eql(8) end it "should return 4 when subtracting 2 from 6" do calculator.subtraction.should eql(4) end end
  • 6. RSpec basics describe MyClass do # creates initial scope before do @object = MyClass.new end describe "#some_method" do # creates new scope it "should ..." do @object.should ... end context "when authorized" do # creates new scope before do @object.authorized = true end
  • 7. RSpec basics (Con.) it "should ..." do @object.should ... end end context "when not authorized" do # creates new scope it "should ..." do @object.should ... end end end it "should ..." do pending "Fix some model first" # creates pending test end end
  • 8. Why RSpec? - Readability - Tests documentation and failure messages
  • 9. Readability describe Campaign do it "should be visible by default" do campaign.should be_visible end it "should have performances visible by default" do campaign.performances_visible.should be_true end it "should not be published at start" do campaign.should_not be_published end
  • 10. Readability (Con.) it "should not be ready to publish at start - without performances, etc" do campaign.should_not be_ready_to_publish end describe "#allowed_performance_kinds" do it "should allow all by default" do campaign.allowed_performance_kinds.should == Performance.kinds end end end
  • 11. Tests documentation and failure messages
  • 12. When tests output matter - Built-in profiler - Test run filters - Conventions - Built-in matchers
  • 14. Test run filters def old_ruby RUBY_VERSION != "1.9.2" end describe TrueClass do it "should be true for true", :if => old_ruby do true.should be_true end it "should be true for String", :current => true do "".should be_true end
  • 15. Test run filters (Cons.) it "should be true for Fixnum" do 0.should be_true end end
  • 17. Built-in matchers target.should satisfy {|arg| ...} target.should_not satisfy {|arg| ...} target.should equal <value> target.should not_equal <value> target.should be_close <value>, <tolerance> target.should_not be_close <value>, <tolerance> target.should be <value> target.should_not be <value> target.should predicate [optional args] target.should be_predicate [optional args] target.should_not predicate [optional args] target.should_not be_predicate [optional args] target.should be < 6 target.should be_between(1, 10)
  • 18. Built-in matchers (Cons.) target.should match <regex> target.should_not match <regex> target.should be_an_instance_of <class> target.should_not be_an_instance_of <class> target.should be_a_kind_of <class> target.should_not be_a_kind_of <class> target.should respond_to <symbol> target.should_not respond_to <symbol> lambda {a_call}.should raise_error lambda {a_call}.should raise_error(<exception> [, message]) lambda {a_call}.should_not raise_error lambda {a_call}.should_not raise_error(<exception> [, message])
  • 19. Built-in matchers (Cons.) proc.should throw <symbol> proc.should_not throw <symbol> target.should include <object> target.should_not include <object> target.should have(<number>).things target.should have_at_least(<number>).things target.should have_at_most(<number>).things target.should have(<number>).errors_on(:field) expect { thing.approve! }.to change(thing, :status) .from(Status::AWAITING_APPROVAL) .to(Status::APPROVED) expect { thing.destroy }.to change(Thing, :count).by(-1)
  • 20. Problems ? - Hard to learn at the beginning - Routing tests could have more detailed failure messages - Rails upgrade can break tests compatibility
  • 21. RSpec in action - Model specs (placed under spec/models director) - Controller specs (placed under spec/controllers directory) - Helper specs (placed under spec/helpers directory) - View specs (placed under spec/views directory) - Routing specs (placed under spec/routing directory)
  • 22. Model specs describe Campaign do it "should be visible by default" do campaign.should be_visible end it "should have performances visible by default" do campaign.performances_visible.should be_true end it "should not be published at start" do campaign.should_not be_published end
  • 23. Model specs (Cons.) it "should not be ready to publish at start - without performances, etc" do campaign.should_not be_ready_to_publish end describe "#allowed_performance_kinds" do it "should allow all by default" do campaign.allowed_performance_kinds.should == Performance.kinds end end end
  • 24. Controller specs describe SessionsController do render_views describe "CREATE" do context "for virtual user" do before do stub_find_user(virtual_user) end it "should not log into peoplejar" do post :create, :user => {:email => virtual_user.email, :password => virtual_user.password } response.should_not redirect_to(myjar_dashboard_path) end end
  • 25. Controller specs (Cons.) context "for regular user" do before do stub_find_user(active_user) end it "should redirect to myjar when login data is correct" do post :create, :user => {:email => active_user.email, :password => active_user.password } response.should redirect_to(myjar_dashboard_path) end end end end
  • 26. Helper specs describe CampaignsHelper do let(:campaign) { Factory.stub(:campaign) } let(:file_name) { "meldung.jpg" } it "should return the same attachment URL as paperclip if there is no attachment" do campaign.stub(:featured_image_file_name).and_return(nil) helper.campaign_attachment_url(campaign, :featured_image). should eql(campaign.featured_image.url) end it "should return the same attachment URL as paperclip if there is attachment" do campaign.stub(:featured_image_file_name).and_return(file_name)
  • 27. Helper specs (Cons.) helper.campaign_attachment_url(campaign, :featured_image). should eql(campaign.featured_image.url) end end
  • 28. View specs # view at views/campaigns/index.html.erb <%= content_for :actions do %> <div id="hb_actions" class="browse_arena"> <div id="middle_actions"> <ul class="btn"> <li class="btn_blue"><%= create_performance_link %></li> </ul> </div> </div> <% end %> <div id="interest_board_holder"> <%= campaings_wall_template(@campaigns) %> </div>
  • 29. View specs (Cons.) # spec at spec/views/campaigns/index.html.erb_spec.rb describe "campaigns/index.html.erb" do let(:campaign) { Factory.stub(:campaign) } it "displays pagination when there are more than 20 published campaigns" do assign(:campaigns, (1..21).map { campaign }. paginate(:per_page => 2) ) render rendered.should include("Prev") rendered.should include("Next") end end
  • 30. Routing specs describe "home routing", :type => :controller do it "should route / to Home#index" do { :get => "/" }.should route_to(:controller => "home", :action => "index", :subdomain => false) end it "should route / with subdomain to Performances::Performances#index do { :get => "http://kzkgop.test.peoplejar.net" }. should route_to(:namespace => nil, :controller => "performances/performances", :action => "index") end end
  • 31. Routing specs (Cons.) describe "error routing", :type => :controller do it "should route not existing route Errors#new" do { :get => "/not_existing_route" }.should route_to(:controller => "errors", :action => "new", :path => "not_existing_route") end End describe "icebreaks routing" do it "should route /myjar/icebreaks/initiated to Icebreaks::InitiatedIcebreaks#index" do { :get => "/myjar/icebreaks/initiated" }.should route_to(:controller => "icebreaks/initiated_icebreaks", :action => "index") end end
  • 32. Routing specs (Cons.) describe "admin routing" do it "should route /admin to Admin::Base#index" do { :get => "/admin" }.should route_to(:controller => "admin/welcome", :action => "index") end end
  • 34. Back to unit test assumptions - A unit is the smallest testable part of an application - The goal of unit testing is to isolate each part of the program and show that the individual parts are correct - Ideally, each test case is independent from the others
  • 35. you.should use_stubs! - Isolate your unit tests from external libraries and dependencies - Propagate skinny methods which has low responsibility - Single bug should make only related tests fail - Speed up tests
  • 37. Are there any problems ? - Writing test is more time consuming - Need to know stubbed library internal implementations - Need to write an integration test first
  • 38. Stubs in action User.stub(:new) # => nil User.stub(:new).and_return(true) user_object = User.new user_object.stub(:save).and_return(true) User.stub(:new).and_return(user_object) user_object.stub(:update_attributes).with(:username => "test"). and_return(true) User.stub(:new).and_return(user_object) User.any_instance.stub(:save).and_return(true) # User.active.paginate User.stub_chain(:active, :paginate).and_return([user_object])
  • 39. Stubs in action User.stub(:new) # => nil User.stub(:new).and_return(true) user_object = User.new user_object.stub(:save).and_return(true) User.stub(:new).and_return(user_object) user_object.stub(:update_attributes).with(:username => "test"). and_return(true) User.stub(:new).and_return(user_object) User.any_instance.stub(:save).and_return(true) # User.active.paginate User.stub_chain(:active, :paginate).and_return([user_object])
  • 40. Stubs in action (Cons.) user_object.stub(:set_permissions).with(an_instance_of(String), anything).and_return(true) user_object.unstub(:set_permissions) # user_object.set_permissions("admin", true) # => true (will use stubbed method) # user_object.set_permissions("admin") # => false (will call real method)
  • 41. Mocks in action User.should_receive(:new) # => nil User.should_receive(:new).and_return(true) User.should_not_receive(:new) user_object = User.new user_object.should_receive(:save).and_return(true) User.stub(:new).and_return(user_object) user_object.should_receive(:update_attributes). with(:username => "test").and_return(true) User.stub(:new).and_return(user_object) User.any_instance.should_receive(:save).and_return(true) # !
  • 42. Mocks in action (Cons.) user_object.should_receive(:update_attributes).once # default user_object.should_receive(:update_attributes).twice user_object.should_receive(:update_attributes).exactly(3).times user_object.should_receive(:set_permissions). with(an_instance_of(String), anything) # user_object.set_permissions("admin", true) # Success # user_object.set_permissions("admin") # Fail
  • 43. What's the difference between Stubs and Mocks - Mocks are used to define expectations and verify them - Stubs allows for defining eligible behavior - Stubs will not cause a test to fail due to unfulfilled expectation
  • 44. In practice - Stub failure describe ".to_csv_file" do it "should generate CSV output" do User.stub(:active).and_return([user]) User.to_csv_file.should == "#{user.display_name},#{user.email}n" end end
  • 45. In practice - Mock failure describe "#facebook_uid=" do it "should build facebook setting instance if not exists when setting uid" do user.should_receive(:build_facebook_setting).with(:uid => "123") user.facebook_uid = "123" end end