Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RSpec (on Rails)

RSpec (on Rails)

After much demand for a talk about RSpec from LRUG members, I stepped up to the bat. This talk introduces RSpec and shows how to use it with Rails instead of the default Test::Unit tests.

Given at the July 2007 LRUG meeting (http://lrug.org/meetings/2007/06/20/july-2007-meeting/). James Mead gave a talk about mocks (http://jamesmead.org/blog/2007-07-22-an-introduction-to-mock-objects-in-ruby) at the same meeting, so I didn't go into any detail there.


Tom Stuart

July 09, 2007


  1. RSpec (on Rails) Tom Stuart tom@experthuman.com

  2. Weasel words Who am I, anyway? Not an expert, just

    a satisfied customer. This is a distillation of public documentation. BDD & RSpec: controversial? YOU DECIDE. This is just an infomercial.
  3. rspec.rubyforge.org davidchelimsky.net behaviour-driven.org dannorth.net

  4. Testing

  5. verifying code correctness “ ”

  6. Ruby: Test::Unit Rails: Test::Unit (+ ActionController::Assertions, fixtures, etc)

  7. 1. Think of some functionality you want 2. Write a

    test for it (and watch it fail) 3. Write the code necessary to pass the test 4. Refactor Toward Deeper Insight™ 5. Goto 1 6. Profit! Test-Driven Development Is this “testing”?
  8. WIKIPEDIA Practitioners emphasize that test-driven development is a method of

    designing software, not merely a method of testing. “ ” Test-driven development From Wikipedia, the free encyclopedia
  9. WIKIPEDIA In linguistics, the Sapir–Whorf hypothesis states that there is

    a systematic relationship between the grammatical categories of the language a person speaks and how that person both understands the world and behaves in it. “ ” Sapir–Whorf hypothesis From Wikipedia, the free encyclopedia
  10. WIKIPEDIA Put simply, the hypothesis argues that the nature of

    a particular language influences the habitual thought of its speakers. Different patterns of language yield different patterns of thought. “ ” Sapir–Whorf hypothesis From Wikipedia, the free encyclopedia
  11. A language that doesn’t affect the way you think about

    programming is not worth knowing. — Alan Perlis “ ”
  12. Test-Driven Development In proper TDD we’re not really verifying code,

    we’re specifying behaviour. But the vocabulary is all testing-oriented: • methods named “test_...” • conditions verified with “assert” • classes called “Test”, “TestCase”, ...
  13. insidious! distracting! too much freedom!

  14. The deeper I got into TDD, the more I felt

    that my own journey had been less of a process of gradual mastery than a series of blind alleys. “
  15. I remember thinking “if only someone had told me that!”

    more often than I thought “wow, a door has opened.”
  16. I decided it must be possible to present TDD in

    a way that gets straight to the good stuff and avoids the pitfalls. My response is behaviour- driven development. — Dan North ”
  17. Behaviour

  18. Behaviour-Driven Development Specify behaviour, don’t verify state. (New names for

    old TDD ideas.) Adds no technical power... ...but adds psychological power through constraints. (& technical convenience through a DSL in RSpec)
  19. Test::Unit RSpec test suites specifications fixtures behaviours tests examples assertions

  20. • ...to do TDD right • ...to write specs that

    also serve as intention-revealing documentation These new words make it easier and more natural...
  21. Anatomy of a specification

  22. behaviour example example

  23. specification

  24. Account when first created - should have a balance of

    £0 - should allow deposits Account when at overdraft limit - should not allow withdrawals Finished in 0.006646 seconds 3 examples, 0 failures
  25. Expectations (Great!)

  26. When RSpec executes specifications, it defines #should and #should_not on

    every object in the system. These methods are your entry to the magic of RSpec. “ ” — rspec.rubyforge.org
  27. RSpec includes many matchers. #should and #should_not take a matcher

    as an argument, and apply it to their receiver.
  28. assert_equal(result, 42) result.should eql(42) result.should == 42

  29. assert(!username.blank?) username.should_not be_blank dynamically-generated predicate matchers with be_, be_a_ and

  30. assert(password.length >= 6) password.should have_at_least(6).characters post.should have_at_most(50).comments sugar

  31. assert_raise(RecordNotSaved) do user.save! end lambda { user.save! }.should raise_error(RecordNotSaved)

  32. ...and many more. (It’s easy to write your own matcher:

    any object that responds to #matches? and #failure_message will work.)
  33. None
  34. None
  35. None
  36. tom:~$ spec --color --format specdoc stack_spec.rb Stack when full -

    should be full - should not be empty - should return the top item when sent #peek - should NOT remove the top item when sent #peek - should return the top item when sent #pop - should remove the top item when sent #pop - should complain on #push (FAILED - 1) 1) 'Stack when full should complain on #push' FAILED expected StackOverflowError but nothing was raised ./stack_spec.rb:46: Finished in 0.007652 seconds 7 examples, 1 failure
  37. None
  38. tom:~$ spec --color --format specdoc stack_spec.rb Stack when full -

    should be full - should not be empty - should return the top item when sent #peek - should NOT remove the top item when sent #peek - should return the top item when sent #pop - should remove the top item when sent #pop - should complain on #push Finished in 0.007514 seconds 7 examples, 0 failures
  39. Mocking (What he said.)

  40. • state-based testing breaks encapsulation and is a barrier to

    refactoring • use mock objects to get away from testing state — test interactions instead • mocking everything (except the object under consideration) greatly reduces dependency and increases isolation • mocks are automatically verified when a spec finishes executing
  41. RSpec has a built-in mocking framework. Mocking is an integral

    part of BDD, but not vice versa. You can already do this interaction-based behavioural testing with Test::Unit and Mocha (or FlexMock, or ...) These mocking frameworks can plug into RSpec too: Spec::Runner.configure do |config| config.mock_with :mocha end
  42. @page = mock('page') @page.should_receive(:path).at_least(:once).and_return('/') Page.should_receive(:find).with("1").and_return(@page) obj.should_receive(:foo).with(an_instance_of(Bar))

  43. sample.caboo.se

  44. Typo

  45. Rails

  46. script/generate rspec_model Account admin:boolean script/generate rspec_controller Dog bark fetch script/generate

    rspec_scaffold Post title:string body:text Spec generators: Custom expectations for models, controllers, views and helpers
  47. Test::Unit RSpec Unit tests Model examples Functional tests View, Controller

    & Helper examples Integration tests ?
  48. Model examples Post.should have(:no).records user.should have(2).errors_on(:password)

  49. Controller examples account = mock_model(Account) Account.should_receive(:find).with(37).and_return(account) response.should be_success response.should redirect_to(‘posts/1’)

    response.should render_template(‘list’) response.should have_text(‘some text’) #id, #to_param, #new_record?, #errors, #is_a?, #kind_of?, #instance_of?, #class
  50. View examples response.should have_tag(‘input#person_address’) article = mock_model(Article) article.should_receive(:author).and_return(‘Joe’) article.should_receive(:text).and_return(‘Hi’) assigns[:article]

    = article assigns[:articles] = [article]
  51. Places to start • rspec.rubyforge.org: tutorials and RDoc • script/generate

    rspec_scaffold • sample.caboo.se, etc • rspec-users
  52. None