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

New features in RSpec 3

Eugene Kenny
September 10, 2014

New features in RSpec 3

Presented internally at Intercom - https://www.intercom.io

Quick rundown of what has changed in RSpec 3, and some of its interesting new features.

Eugene Kenny

September 10, 2014
Tweet

More Decks by Eugene Kenny

Other Decks in Technology

Transcript

  1. 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
  2. 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
  3. 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
  4. 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
  5. Verifying doubles •Only allow you to stub methods that exist

    •Harder to write tests that pass with broken code
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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 <top (required)>'
  11. 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
  12. 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
  13. Compound expectations •Chain expectations together with `and` / `or` •Reduce

    duplication, group related expectations •Easy to abuse, but useful in some cases
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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]