Rspec 101Jason Noblehttp://jasonnoble.org
Example Groupdescribe()Defines example group of testsString we pass describes the item we’re testingit()Defines a code exampleString we pass describes the specific behaviour
describe() Methoddescribe “A User” {…}A Userdescribe User {…}Userdescribe User, “with no roles assigned” {…}User with no roles assigned
Describe blocks can be nested
context() methodcontext() is an alias for describe()Use describe() for things, context() for context
it() methodArgument should state what is being tested
Pending testsWe can mark tests to be implemented “later”
Pending tests (cont.)Each method of marking a test as pending has its usefulness:Add pending examples as you think of stuff to testDisable failing examples without losing track that you need to fix those at some pointWrap failing examples when you want to be notified when changes to the system cause them to pass (bug is fixed, etc)
before()/after() methodBefore/after methods helps you set and/or reset initial stateCreate a new stack, add one element to itTakes one argument:eachExecutes this block before each test group executes:allExecutes this block once for all tests before the first test is run
before(:each) method
before(:all)Method is run once and only once for a group of testsBe careful using this method, usually we want each test to have it’s own environment setupSharing state between examples can cause unexpected thingsGood examples:Opening a network connectionPre-seeding caches
after(:each) methodCode is ran after each exampleRarely necessary because each example runs in its own scope, and consequently the instance variables in that scope are resetCan be useful to reset global state of things after your test completesafter(:each) is guaranteed to run after each example, even if failure or errors are raised
after(:each) example
after(:all) methodThis is even more rare than the after(:each)Examples:Close down browsersClose database connectionsClose socketsAny resource we want to release when we’re done, but not after every individual test
around(:each) methodSupports APIs that require a blockVery rarely, if ever usedI have never used thisPut your functionality into before/after blocks if at all possibleSee http://relishapp.com/rspec/rspec-core/v/2-0/dir/hooks/around-hooks if you’re interested
Helper MethodsDefined within an example groupAvailable to all examples in the group
Shared Helper MethodsIf helper methods need to be used across example groups, put them in one or more modules and include modules in example groups we want to have access
Shared ExamplesIf we expect instances of more than one class to behave in the same way, a shared example group describes the behavior once and includes it in multiple example groups
Shared examples (cont.)
RSpec::ExpectationsOne goal of BDD is getting the words rightExpectations vs. AssertionsWe are setting an expectation of what should happen rather than what will happenIn fact the word should is part of RSpecresult.should equal(5)message.should match(/on Sunday/)
should, should_not and matchersresult.should equal(5)If result is equal to 5, it passesresult.should_not equal(5)If result is anything other than 5, it passesGeneral Pattern:result.should   ________(value)_______ is a matcher
RSpec built in Matchersinclude(item)prime_numbers.should_not include(8)respond_to(message)list.shouldrespond_to(:length)raise_error(type)lambda { Object.new.explode! }.should raise_error(NameError)
4 ways to be equala == bValue equality (Most common)a === bIs Object a the same Object as ba.eql?(b)Are a and b values equal and of same typea.equal?(b)Is Object a the same Object as b(General Rule: The longer the method name, the more restrictive the matcher is)
Do not use != in expectationsactual.should != expectedaction.should_not == expectedCauses issues, explained in detail in the RSpec book
Floating Point Calculations“expected 5.25 got 5.251” is frustrating in a failure messageRSpec offers a be_close matcher that accepts an expected value and an acceptable deltaresult.shouldbe_close(5.25, 0.005)Will pass as long as result is within .005 of 5.25
Matching Textresponse.should match(/this expression/)Matches if response has text “this expression” somewhere in its contentsresponse.should =~ /this expression/Functionally equivalent to the previous one
Expect{}Tests that a block of code causes some effectYou can also use .to(1) or .from(0).to(1)
Predicate MatchersHow do we test array.empty?array.empty?.should == truearray.shouldbe_empty
Predicate matchers (cont.)RSpec gives you other optionsbe_be_ated.shouldbe_a_kind_of(Player)  => ted.kind_of?(Player)be_anted.shouldbe_an_instance_of(Player) =>ted.instance_of?(Player)RSpec also lets you write your own matchers
Matchershave_ becomes has_request_params.shouldhave_key(:id) =>request_params.has_key?(:id).should == true
specify{}Sometimes RSpec can guess your testsit “should have 32 pieces” do	@board.should have(32).piecesendspecify { @board.should have(32).pieces }This is used rarely (very simple tests)
subject {}Clean up your tests by specifying a subject
RSpec MocksMocks allow you to fake functionality that isn’t being tested.  See the book for more info.
rspec commandrspec –helpList options available to running RSpecrspec spec/simple_math_spec.rbRun only one spec filerspec specRun all specs in spec/ directoryrspec spec --format documentationMakes RSpec more verbose with test output
rspec commands (cont.)rspec spec –colorPassing is green, pending is yellow, fail is redStore common options in .rspec file--color--format documentationOptions stored in ./.rspec take precedence over ~/.rspec, options declared command line win
Let’s get startedmkdir -p calculator/{lib,spec}cd calculatormate .
spec/calculator_spec.rblib/calculator.rb
spec/calculator_spec.rb

Rspec 101

  • 1.
  • 2.
    Example Groupdescribe()Defines examplegroup of testsString we pass describes the item we’re testingit()Defines a code exampleString we pass describes the specific behaviour
  • 3.
    describe() Methoddescribe “AUser” {…}A Userdescribe User {…}Userdescribe User, “with no roles assigned” {…}User with no roles assigned
  • 4.
  • 5.
    context() methodcontext() isan alias for describe()Use describe() for things, context() for context
  • 6.
    it() methodArgument shouldstate what is being tested
  • 7.
    Pending testsWe canmark tests to be implemented “later”
  • 8.
    Pending tests (cont.)Eachmethod of marking a test as pending has its usefulness:Add pending examples as you think of stuff to testDisable failing examples without losing track that you need to fix those at some pointWrap failing examples when you want to be notified when changes to the system cause them to pass (bug is fixed, etc)
  • 9.
    before()/after() methodBefore/after methodshelps you set and/or reset initial stateCreate a new stack, add one element to itTakes one argument:eachExecutes this block before each test group executes:allExecutes this block once for all tests before the first test is run
  • 10.
  • 11.
    before(:all)Method is runonce and only once for a group of testsBe careful using this method, usually we want each test to have it’s own environment setupSharing state between examples can cause unexpected thingsGood examples:Opening a network connectionPre-seeding caches
  • 12.
    after(:each) methodCode isran after each exampleRarely necessary because each example runs in its own scope, and consequently the instance variables in that scope are resetCan be useful to reset global state of things after your test completesafter(:each) is guaranteed to run after each example, even if failure or errors are raised
  • 13.
  • 14.
    after(:all) methodThis iseven more rare than the after(:each)Examples:Close down browsersClose database connectionsClose socketsAny resource we want to release when we’re done, but not after every individual test
  • 15.
    around(:each) methodSupports APIsthat require a blockVery rarely, if ever usedI have never used thisPut your functionality into before/after blocks if at all possibleSee http://relishapp.com/rspec/rspec-core/v/2-0/dir/hooks/around-hooks if you’re interested
  • 16.
    Helper MethodsDefined withinan example groupAvailable to all examples in the group
  • 17.
    Shared Helper MethodsIfhelper methods need to be used across example groups, put them in one or more modules and include modules in example groups we want to have access
  • 18.
    Shared ExamplesIf weexpect instances of more than one class to behave in the same way, a shared example group describes the behavior once and includes it in multiple example groups
  • 19.
  • 20.
    RSpec::ExpectationsOne goal ofBDD is getting the words rightExpectations vs. AssertionsWe are setting an expectation of what should happen rather than what will happenIn fact the word should is part of RSpecresult.should equal(5)message.should match(/on Sunday/)
  • 21.
    should, should_not andmatchersresult.should equal(5)If result is equal to 5, it passesresult.should_not equal(5)If result is anything other than 5, it passesGeneral Pattern:result.should ________(value)_______ is a matcher
  • 22.
    RSpec built inMatchersinclude(item)prime_numbers.should_not include(8)respond_to(message)list.shouldrespond_to(:length)raise_error(type)lambda { Object.new.explode! }.should raise_error(NameError)
  • 23.
    4 ways tobe equala == bValue equality (Most common)a === bIs Object a the same Object as ba.eql?(b)Are a and b values equal and of same typea.equal?(b)Is Object a the same Object as b(General Rule: The longer the method name, the more restrictive the matcher is)
  • 24.
    Do not use!= in expectationsactual.should != expectedaction.should_not == expectedCauses issues, explained in detail in the RSpec book
  • 25.
    Floating Point Calculations“expected5.25 got 5.251” is frustrating in a failure messageRSpec offers a be_close matcher that accepts an expected value and an acceptable deltaresult.shouldbe_close(5.25, 0.005)Will pass as long as result is within .005 of 5.25
  • 26.
    Matching Textresponse.should match(/thisexpression/)Matches if response has text “this expression” somewhere in its contentsresponse.should =~ /this expression/Functionally equivalent to the previous one
  • 27.
    Expect{}Tests that ablock of code causes some effectYou can also use .to(1) or .from(0).to(1)
  • 28.
    Predicate MatchersHow dowe test array.empty?array.empty?.should == truearray.shouldbe_empty
  • 29.
    Predicate matchers (cont.)RSpecgives you other optionsbe_be_ated.shouldbe_a_kind_of(Player) => ted.kind_of?(Player)be_anted.shouldbe_an_instance_of(Player) =>ted.instance_of?(Player)RSpec also lets you write your own matchers
  • 30.
    Matchershave_ becomes has_request_params.shouldhave_key(:id)=>request_params.has_key?(:id).should == true
  • 31.
    specify{}Sometimes RSpec canguess your testsit “should have 32 pieces” do @board.should have(32).piecesendspecify { @board.should have(32).pieces }This is used rarely (very simple tests)
  • 32.
    subject {}Clean upyour tests by specifying a subject
  • 33.
    RSpec MocksMocks allowyou to fake functionality that isn’t being tested. See the book for more info.
  • 34.
    rspec commandrspec –helpListoptions available to running RSpecrspec spec/simple_math_spec.rbRun only one spec filerspec specRun all specs in spec/ directoryrspec spec --format documentationMakes RSpec more verbose with test output
  • 35.
    rspec commands (cont.)rspecspec –colorPassing is green, pending is yellow, fail is redStore common options in .rspec file--color--format documentationOptions stored in ./.rspec take precedence over ~/.rspec, options declared command line win
  • 36.
    Let’s get startedmkdir-p calculator/{lib,spec}cd calculatormate .
  • 37.
  • 38.

Editor's Notes

  • #29 Thebe_XXXX test works only if the item you called should on has a XXXX? method.
  • #33 If the should method doesn’t have an explicit receiver, it will delegate to the declared subject.Read your tests out loud to make sure they sound right. “Specify the subject should be eligible to vote” vs “it should be eligible to vote”.
  • #34 should_receive will error if :name is not calledstub will not error if :occupation is not called