Testing with RSpec (RubyDay 2011)

Post on 06-May-2015

2807 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

The slides of my "Testing with RSpec" talk at RubyDay 2011.

Transcript

Simone Carlettisimone@carletti.name

twitter: @weppos

Testing with RSpec

2011

Monday, June 20, 2011

Me.about(:robodomain)

Monday, June 20, 2011

Me.about(:ifad)

Monday, June 20, 2011

describe RSpec, "Agenda"

Monday, June 20, 2011

Monday, June 20, 2011

Monday, June 20, 2011

Testing

Monday, June 20, 2011

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

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

Monday, June 20, 2011

$ rspec

http://relishapp.com/rspec

https://github.com/rspec

Monday, June 20, 2011

Monday, June 20, 2011

describe RSpec, "Features"

Monday, June 20, 2011

Readabilitydescribe 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("weppos@weppos.net") }.to raise_error(Whois::ServerNotSupported) end endend

Monday, June 20, 2011

Readabilitydescribe 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("weppos@weppos.net") }.to raise_error(Whois::ServerNotSupported) end endend

Monday, June 20, 2011

Isolationdescribe 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 endend

Monday, June 20, 2011

Extensibledescribe 9 do it "should be a multiple of 3" do (9 % 3).should == 0 endend

# vs

RSpec::Matchers.define :be_a_multiple_of do |expected| match do |actual| actual % expected == 0 endend

describe 9 do it "should be a multiple of 3" do 9.should be_a_multiple_of(3) endend

Monday, June 20, 2011

describe RSpec, "Syntax"

Monday, June 20, 2011

Basic Exampledescribe 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 endend

Monday, June 20, 2011

Basic Exampledescribe 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 endend

Monday, June 20, 2011

context / describedescribe "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 endend

Monday, June 20, 2011

before / afterdescribe "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

before / afterdescribe "before / after" do before(:each) do @hash = {} end

it "does something" do @hash[:key] = "value" @hash[:key].should_not be_empty endend

Monday, June 20, 2011

let$count = 0describe "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 endend

Monday, June 20, 2011

Implicit subjectdescribe Array do describe "when first created" do it "should be empty" do subject.should eq([]) end endend

Monday, June 20, 2011

subjectdescribe Array do subject { [1,2,3] }

describe "with some elements" do it "should have the prescribed elements" do subject.should == [1,2,3] end endend

Monday, June 20, 2011

expectdescribe Integer do describe "#/" do it "raises ZeroDivisionError when divisor is 0" do lambda do 1 / 0 end.should raise_error(ZeroDivisionError) end endend

# vs

describe Integer do describe "#/" do it "raises ZeroDivisionError when divisor is 0" do expect { 1 / 0 }.to raise_error(ZeroDivisionError) end endend

Monday, June 20, 2011

Shared Behaviorsshared_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 endend

Monday, June 20, 2011

Shared Behaviorsdescribe Array do it_behaves_like "a collection"end

describe Set do it_behaves_like "a collection"end

Monday, June 20, 2011

pendingdescribe 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" endend

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 endend

Monday, June 20, 2011

pendingdescribe 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" endend

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 endend

Monday, June 20, 2011

pendingdescribe "an example" do xit "is pending using xit" do true.should be(true) endend

describe "an example" do it "checks something" do (3+4).should == 7 end pending do "string".reverse.should == "gnirts" endend

Monday, June 20, 2011

Metadatadescribe "something", :this => { :is => :arbitrary } do it "does something", :and => "so is this" do # ... endend

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 # .... endend

Monday, June 20, 2011

MetadataRSpec.configure do |c| c.filter_run :focus => trueend

describe "something", :focus => true do it "does something" do # .... endend

RSpec.configure do |c| c.filter_run_excluding :slow => trueend

describe "something", :slow => true do it "does something" do # .... endend

Monday, June 20, 2011

describe RSpec, "Integration"

Monday, June 20, 2011

RSpec

Monday, June 20, 2011

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 endend

RSpec# Gemfilegroup :development, :test do gem 'rspec-rails', '~> 2.6.0'end

spec folder

Monday, June 20, 2011

RSpecRSpec.configure do |config| config.include Rack::Test::Methodsend

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 endend

rack-test

Monday, June 20, 2011

RSpec MochaRR

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

RSpec Factoriesdescribe 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') } endend

Monday, June 20, 2011

describe RSpec, "Best Practices"

Monday, June 20, 2011

describe vs contextdescribe ".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 endend

Monday, June 20, 2011

it vs specifydescribe User do before do @user = User.new :username => 'weppos' end

it "is not an administrator" do @user.should_not be_admin endend

# vs.

describe User do let(:user) do User.new :username => 'weppos' end

specify do user.should_not be_admin endend

Monday, June 20, 2011

shoulddescribe "#order_price" do it "should be a Money" do # ... end

it "should return nil when amount is blank" do # ... endend

# vs

describe "#order_price" do it "is a Money" do # ... end

it "returns nil when amount is blank" do # ... endend

Monday, June 20, 2011

context vs itdescribe 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 endend

# 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 endend

Monday, June 20, 2011

Instance vs Class methodsdescribe 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 endend

Monday, June 20, 2011

spec_helper.rb & /support

Monday, June 20, 2011

Demo Time

Monday, June 20, 2011

Choose the right tool...

Monday, June 20, 2011

... and Test!

Monday, June 20, 2011

Thanks!

Monday, June 20, 2011

top related