Slide 1

Slide 1 text

Simone Carletti [email protected] twitter: @weppos Testing with RSpec 2011 Monday, June 20, 2011

Slide 2

Slide 2 text

Me.about(:robodomain) Monday, June 20, 2011

Slide 3

Slide 3 text

Me.about(:ifad) Monday, June 20, 2011

Slide 4

Slide 4 text

describe RSpec, "Agenda" Monday, June 20, 2011

Slide 5

Slide 5 text

Monday, June 20, 2011

Slide 6

Slide 6 text

Monday, June 20, 2011

Slide 7

Slide 7 text

Testing Monday, June 20, 2011

Slide 8

Slide 8 text

TDD seems to be applicable in various domains and can significantly reduce the defect density of developed software without significant productivity reduction of the development team. http://www.infoq.com/news/2009/03/TDD-Improves- Quality Monday, June 20, 2011

Slide 9

Slide 9 text

The pre-release defect density of the four products, measured as defects per thousand lines of code, decreased between 40% and 90% relative to the projects that did not use TDD. The teams' management reported subjectively a 15–35% increase in initial development time for the teams using TDD, though the teams agreed that this was offset by reduced maintenance costs. Monday, June 20, 2011

Slide 10

Slide 10 text

Monday, June 20, 2011

Slide 11

Slide 11 text

$ rspec http://relishapp.com/rspec https://github.com/rspec Monday, June 20, 2011

Slide 12

Slide 12 text

Monday, June 20, 2011

Slide 13

Slide 13 text

describe RSpec, "Features" Monday, June 20, 2011

Slide 14

Slide 14 text

Readability describe Whois::Client do describe "#initialize" do it "accepts a timeout setting with a value in seconds" do klass.new(:timeout => 100).timeout.should == 100 end it "accepts a timeout setting with a nil value" do klass.new(:timeout => nil).timeout.should be_nil end end describe "#query" do it "detects email" do expect { klass.new.query("[email protected]") }.to raise_error(Whois::ServerNotSupported) end end end Monday, June 20, 2011

Slide 15

Slide 15 text

Readability describe Whois::Client do describe "#initialize" do it "accepts a timeout setting with a value in seconds" do klass.new(:timeout => 100).timeout.should == 100 end it "accepts a timeout setting with a nil value" do klass.new(:timeout => nil).timeout.should be_nil end end describe "#query" do it "detects email" do expect { klass.new.query("[email protected]") }.to raise_error(Whois::ServerNotSupported) end end end Monday, June 20, 2011

Slide 16

Slide 16 text

Isolation describe UsersController do context "when logged as administrator" do before(:each) do log_in_as @admin = Factory(:user, :username => "admin") end context "with a bunch of users" do before(:each) { @users = 3.times.map { Factory(:user) } } describe "GET index" end context "with no users" do describe "GET new" describe "POST create" end describe "with a user" do before(:each) { @user = Factory(:user) } describe "GET show" end end end Monday, June 20, 2011

Slide 17

Slide 17 text

Extensible describe 9 do it "should be a multiple of 3" do (9 % 3).should == 0 end end # vs RSpec::Matchers.define :be_a_multiple_of do |expected| match do |actual| actual % expected == 0 end end describe 9 do it "should be a multiple of 3" do 9.should be_a_multiple_of(3) end end Monday, June 20, 2011

Slide 18

Slide 18 text

describe RSpec, "Syntax" Monday, June 20, 2011

Slide 19

Slide 19 text

Basic Example describe String do describe "#upcase" do it "returns the original string converted to uppercase" do "hello".upcase.should == "HELLO" end it "returns a String" do "hello".upcase.should be_a(String) end it "does not change the original string" do string = "hello" string.upcase string.should_not == "HELLO" end end end Monday, June 20, 2011

Slide 20

Slide 20 text

Basic Example describe String do describe "#upcase" do it "returns the original string converted to uppercase" do "hello".upcase.should == "HELLO" end it "returns a String" do "hello".upcase.should be_a(String) end it "does not change the original string" do string = "hello" string.upcase string.should_not == "HELLO" end end end Monday, June 20, 2011

Slide 21

Slide 21 text

context / describe describe "something" do context "in one context" do it "does one thing" do end end context "in another context" do it "does another thing" do end end end Monday, June 20, 2011

Slide 22

Slide 22 text

before / after describe "before / after" do before(:each) {} before(:all) {} before(:suite) {} after(:each) {} after(:all) {} after(:suite) {} end # Before and after blocks are called in the following order: # # before suite # before all # before each # after each # after all # after suite Monday, June 20, 2011

Slide 23

Slide 23 text

before / after describe "before / after" do before(:each) do @hash = {} end it "does something" do @hash[:key] = "value" @hash[:key].should_not be_empty end end Monday, June 20, 2011

Slide 24

Slide 24 text

let $count = 0 describe "let" do let(:count) { $count += 1 } it "memoizes the value" do count.should == 1 count.should == 1 end it "is not cached across examples" do count.should == 2 end end Monday, June 20, 2011

Slide 25

Slide 25 text

Implicit subject describe Array do describe "when first created" do it "should be empty" do subject.should eq([]) end end end Monday, June 20, 2011

Slide 26

Slide 26 text

subject describe Array do subject { [1,2,3] } describe "with some elements" do it "should have the prescribed elements" do subject.should == [1,2,3] end end end Monday, June 20, 2011

Slide 27

Slide 27 text

expect describe Integer do describe "#/" do it "raises ZeroDivisionError when divisor is 0" do lambda do 1 / 0 end.should raise_error(ZeroDivisionError) end end end # vs describe Integer do describe "#/" do it "raises ZeroDivisionError when divisor is 0" do expect { 1 / 0 }.to raise_error(ZeroDivisionError) end end end Monday, June 20, 2011

Slide 28

Slide 28 text

Shared Behaviors shared_examples "a collection" do let(:collection) { described_class.new([7, 2, 4]) } context "initialized with 3 items" do it "says it has three items" do collection.size.should eq(3) end end describe "#include?" do context "with an an item that is in the collection" do it "returns true" do collection.include?(7).should be_true end end context "with an an item that is not in the collection" do it "returns false" do collection.include?(9).should be_false end end end end Monday, June 20, 2011

Slide 29

Slide 29 text

Shared Behaviors describe Array do it_behaves_like "a collection" end describe Set do it_behaves_like "a collection" end Monday, June 20, 2011

Slide 30

Slide 30 text

pending describe Hash do it "adds a key" do { :key => "value" }[:key].should == "value" end it "does something unknown" it "does something buggy" do pending "This method is buggy" end end describe Integer do before(:each) { pending } it "computes 1 + 1" do (1 + 1).should == 2 end it "computes 1 - 1" do (1 - 1).should == 0 end end Monday, June 20, 2011

Slide 31

Slide 31 text

pending describe Hash do it "adds a key" do { :key => "value" }[:key].should == "value" end it "does something unknown" it "does something buggy" do pending "This method is buggy" end end describe Integer do before(:each) { pending } it "computes 1 + 1" do (1 + 1).should == 2 end it "computes 1 - 1" do (1 - 1).should == 0 end end Monday, June 20, 2011

Slide 32

Slide 32 text

pending describe "an example" do xit "is pending using xit" do true.should be(true) end end describe "an example" do it "checks something" do (3+4).should == 7 end pending do "string".reverse.should == "gnirts" end end Monday, June 20, 2011

Slide 33

Slide 33 text

Metadata describe "something", :this => { :is => :arbitrary } do it "does something", :and => "so is this" do # ... end end RSpec.configure do |c| c.filter_run_excluding :ruby => lambda {|version| !(RUBY_VERSION.to_s =~ /^#{version.to_s}/) } end describe "something" do it "does something", :ruby => 1.8 do # .... end it "does something", :ruby => 1.9 do # .... end end Monday, June 20, 2011

Slide 34

Slide 34 text

Metadata RSpec.configure do |c| c.filter_run :focus => true end describe "something", :focus => true do it "does something" do # .... end end RSpec.configure do |c| c.filter_run_excluding :slow => true end describe "something", :slow => true do it "does something" do # .... end end Monday, June 20, 2011

Slide 35

Slide 35 text

describe RSpec, "Integration" Monday, June 20, 2011

Slide 36

Slide 36 text

RSpec Monday, June 20, 2011

Slide 37

Slide 37 text

describe AccountsController do describe "GET show" do before(:each) do get :show end it { should respond_with :success } it { should render_template "accounts/show" } it "assigns user" do assigns(:user).should == @user end end end RSpec # Gemfile group :development, :test do gem 'rspec-rails', '~> 2.6.0' end spec folder Monday, June 20, 2011

Slide 38

Slide 38 text

RSpec RSpec.configure do |config| config.include Rack::Test::Methods end describe Tremonti::Application do def app Tremonti::Application end describe "/" do it "should respond with 200" do get "/" last_response.should be_ok last_response.body.should == "Hello from RoboFinance!" end end end rack-test Monday, June 20, 2011

Slide 39

Slide 39 text

RSpec Mocha RR RSpec.configure do |config| # == Mock Framework # # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: # config.mock_with :mocha # config.mock_with :flexmock # config.mock_with :rr end Monday, June 20, 2011

Slide 40

Slide 40 text

RSpec Factories describe BillingCharge do subject { Factory.build(:billing_charge) } describe "associations" do it { should belong_to(:user) } end describe "validations" do it "is valid with factory" do Factory.build(:billing_charge).should be_valid end it { should validate_presence_of(:user_id) } it { should validate_format_of(:period).with('2010-02') } it { should validate_format_of(:period).not_with('201002') } it { should validate_format_of(:period).not_with('2010') } end end Monday, June 20, 2011

Slide 41

Slide 41 text

describe RSpec, "Best Practices" Monday, June 20, 2011

Slide 42

Slide 42 text

describe vs context describe ".guess" do it "recognizes tld" do s = Whois::Server.guess(".com") s.should be_a(Whois::Server::Adapters::Base) s.type.should == :tld end context "when the input is a tld" do it "returns a IANA adapter" do Whois::Server.guess(".com").should == Whois::Server.factory(:tld, ".", "whois.iana.org") end end context "when the input is a domain" do it "lookups definitions and returns the adapter" do Whois::Server.guess("example.test").should == Whois::Server.factory(:tld, ".test", "whois.test") end end end Monday, June 20, 2011

Slide 43

Slide 43 text

it vs specify describe User do before do @user = User.new :username => 'weppos' end it "is not an administrator" do @user.should_not be_admin end end # vs. describe User do let(:user) do User.new :username => 'weppos' end specify do user.should_not be_admin end end Monday, June 20, 2011

Slide 44

Slide 44 text

should describe "#order_price" do it "should be a Money" do # ... end it "should return nil when amount is blank" do # ... end end # vs describe "#order_price" do it "is a Money" do # ... end it "returns nil when amount is blank" do # ... end end Monday, June 20, 2011

Slide 45

Slide 45 text

context vs it describe Hash, "#size" do it "returns 0 when the Hash is empty" do end it "returns the number of elements when the Hash is not empty" do end end # vs describe Hash, "#size" do context "when the Hash is empty" do it "returns 0" do end end context "when the Hash is not empty" do it "returns the number of elements" do end end end Monday, June 20, 2011

Slide 46

Slide 46 text

Instance vs Class methods describe Time do describe ".parse" do it "converts given string into a Time" do Time.parse('2010-06-12').should == Time.mktime(2010, 06, 12) end end describe "#to_i" do it "returns the timestamp corresponding to the Time object" do t = Time.mktime(2010, 06, 12) t.to_i.should == 1276293600 end end end Monday, June 20, 2011

Slide 47

Slide 47 text

spec_helper.rb & /support Monday, June 20, 2011

Slide 48

Slide 48 text

Demo Time Monday, June 20, 2011

Slide 49

Slide 49 text

Choose the right tool... Monday, June 20, 2011

Slide 50

Slide 50 text

... and Test! Monday, June 20, 2011

Slide 51

Slide 51 text

Thanks! Monday, June 20, 2011