Slide 1

Slide 1 text

RSpec (on Rails) Tom Stuart [email protected]

Slide 2

Slide 2 text

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.

Slide 3

Slide 3 text

rspec.rubyforge.org davidchelimsky.net behaviour-driven.org dannorth.net

Slide 4

Slide 4 text

Testing

Slide 5

Slide 5 text

verifying code correctness “ ”

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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”?

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

A language that doesn’t affect the way you think about programming is not worth knowing. — Alan Perlis “ ”

Slide 12

Slide 12 text

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”, ...

Slide 13

Slide 13 text

insidious! distracting! too much freedom!

Slide 14

Slide 14 text

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. “

Slide 15

Slide 15 text

I remember thinking “if only someone had told me that!” more often than I thought “wow, a door has opened.”

Slide 16

Slide 16 text

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 ”

Slide 17

Slide 17 text

Behaviour

Slide 18

Slide 18 text

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)

Slide 19

Slide 19 text

Test::Unit RSpec test suites specifications fixtures behaviours tests examples assertions expectations

Slide 20

Slide 20 text

• ...to do TDD right • ...to write specs that also serve as intention-revealing documentation These new words make it easier and more natural...

Slide 21

Slide 21 text

Anatomy of a specification

Slide 22

Slide 22 text

behaviour example example

Slide 23

Slide 23 text

specification

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Expectations (Great!)

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

RSpec includes many matchers. #should and #should_not take a matcher as an argument, and apply it to their receiver.

Slide 28

Slide 28 text

assert_equal(result, 42) result.should eql(42) result.should == 42

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

...and many more. (It’s easy to write your own matcher: any object that responds to #matches? and #failure_message will work.)

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

Mocking (What he said.)

Slide 40

Slide 40 text

• 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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

@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))

Slide 43

Slide 43 text

sample.caboo.se

Slide 44

Slide 44 text

Typo

Slide 45

Slide 45 text

Rails

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

Test::Unit RSpec Unit tests Model examples Functional tests View, Controller & Helper examples Integration tests ?

Slide 48

Slide 48 text

Model examples Post.should have(:no).records user.should have(2).errors_on(:password)

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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]

Slide 51

Slide 51 text

Places to start • rspec.rubyforge.org: tutorials and RDoc • script/generate rspec_scaffold • sample.caboo.se, etc • rspec-users

Slide 52

Slide 52 text

No content