Slide 1

Slide 1 text

New features in RSpec 3

Slide 2

Slide 2 text

Announced July 2013

Slide 3

Slide 3 text

Released June 2014

Slide 4

Slide 4 text

Old mock and expectation syntax it "uses the old should-based syntax" do message.stub(:sent_at).and_return(Time.now) message.should_receive(:update_stats).with(:some, :args) message.send_count.should == 10 lambda { message.deliver }.should_not raise_error end

Slide 5

Slide 5 text

New mock and expectation syntax it "uses the new expect and allow based syntax" do allow(message).to receive(:sent_at).and_return(Time.now) expect(message).to receive(:update_stats).with(:some, :args) expect(message.send_count).to eq(10) expect { message.deliver }.to_not raise_error end

Slide 6

Slide 6 text

Old boolean matchers it "had boolean matchers that weren't clear" do # matches the literal value TRUE user.paying_customer?.should be_true # also matches any truthy value user.price_plan.should be_true end

Slide 7

Slide 7 text

New boolean matchers it "clarified how they work by renaming them" do expect(user.paying_customer?).to be_truthy expect(user.price_plan).to be_truthy # only matches literal boolean values expect(user.paying_customer?).to be(true) end

Slide 8

Slide 8 text

Verifying doubles •Only allow you to stub methods that exist •Harder to write tests that pass with broken code

Slide 9

Slide 9 text

class Message def initialize(recipients) @recipients = recipients end def email_addresses @recipients.map(&:email) end end class MessageSender def initialize(message) @message = message end def deliver_message @message.emails.each do |email| deliver_to_email(email) end end def deliver_to_email(email) # snip end end

Slide 10

Slide 10 text

class Message def initialize(recipients) @recipients = recipients end def email_addresses @recipients.map(&:email) end end class MessageSender def initialize(message) @message = message end def deliver_message @message.emails.each do |email| deliver_to_email(email) end end def deliver_to_email(email) # snip end end

Slide 11

Slide 11 text

Using a test double it "sends to each of the recipients" do message = double('Message', :emails => ['one', 'two']) message_sender = MessageSender.new(message) expect(message_sender).to receive(:deliver_to_email).with('one') expect(message_sender).to receive(:deliver_to_email).with('two') message_sender.deliver_message end

Slide 12

Slide 12 text

Using an instance double it "sends to each of the recipients" do message = instance_double('Message', :emails => ['one', 'two']) message_sender = MessageSender.new(message) expect(message_sender).to receive(:deliver_to_email).with('one') expect(message_sender).to receive(:deliver_to_email).with('two') message_sender.deliver_message end

Slide 13

Slide 13 text

Using an instance double 4) MessageSender#deliver_message sends to each of the recipients Failure/Error: message = instance_double('Message', :emails => ['one', 'two']) Message does not implement: emails # ./instance_double_spec.rb:6:in `block (3 levels) in '

Slide 14

Slide 14 text

Using a partial double it "sends to each of the recipients" do message = Message.new([]) expect(message).to receive(:emails).and_return(['one', 'two']) message_sender = MessageSender.new(message) expect(message_sender).to receive(:deliver_to_email).with('one') expect(message_sender).to receive(:deliver_to_email).with('two') message_sender.deliver_message end

Slide 15

Slide 15 text

Verifying partial doubles RSpec.configure do |config| config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true end end # Or, if you’re in a hurry RSpec::Mocks.configuration.verify_partial_doubles = true

Slide 16

Slide 16 text

Compound expectations •Chain expectations together with `and` / `or` •Reduce duplication, group related expectations •Easy to abuse, but useful in some cases

Slide 17

Slide 17 text

Without compound expectations it "creates a user" do expect { app.create_user_and_message }.to change(User, :count).by(1) end it "creates a message" do expect { app.create_user_and_message }.to change(Message, :count).by(1) end

Slide 18

Slide 18 text

With compound expectations it "creates a user and a message" do expect { app.create_user_and_message }.to change(User, :count).by(1) .and change(Message, :count).by(1) end

Slide 19

Slide 19 text

Composable matchers •Describe nested structures using matchers •Less brittle, less tedious to write expectations

Slide 20

Slide 20 text

Without composable matchers it "returns two auto messages and a manual message" do messages = app.messages expect(messages[0]).to be_an(AutoMessage) expect(messages[1]).to be_an(AutoMessage) expect(messages[2]).to be_a(ManualMessage) end

Slide 21

Slide 21 text

With composable matchers it "returns two auto messages and a manual message" do messages = app.messages expect(messages).to contain_exactly( an_instance_of(AutoMessage), an_instance_of(AutoMessage), an_instance_of(ManualMessage) ) end

Slide 22

Slide 22 text

Are we running it yet? intercom — RSpec 3.1 muster — RSpec 2.99 ami-builder — RSpec 3.0 banjaxed — RSpec 3.1 pulse — RSpec 3.0 engage — RSpec 3.0 intercom-user-service — RSpec 2.99 intercom-messages-service — RSpec 3.1 intersearch — RSpec 3.1 hustle — RSpec 3.0 muster-builder — RSpec 3.0 muster-deployer — RSpec 3.0 [REDACTED] [REDACTED]

Slide 23

Slide 23 text

http://myronmars.to/n/dev-blog Read Myron Marston’s blog

Slide 24

Slide 24 text

https://twitter.com/rspec Follow RSpec on Twitter