$30 off During Our Annual Pro Sale. View Details »

RSpec 3 and why I `expect(you).to care`

RSpec 3 and why I `expect(you).to care`

Red Dot Ruby Conf 2014 talk about the decisions taken to create RSpec 3

Jon Rowe

June 27, 2014
Tweet

More Decks by Jon Rowe

Other Decks in Programming

Transcript

  1. RSPEC 3 AND WHY I
    expect(you).to care

    View Slide

  2. BEFORE(:TALK)

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. RSPEC 3 AND WHY I
    expect(you).to care

    View Slide

  7. SEMANTIC VERSIONING

    View Slide

  8. HISTORY

    View Slide

  9. RSPEC 0.x

    View Slide

  10. RSPEC 1.x

    View Slide

  11. RSPEC 2.x

    View Slide

  12. RSPEC 3.x

    View Slide

  13. I EXPECT(YOU).TO CARE

    View Slide

  14. WHY YOU.SHOULD CARE

    View Slide

  15. class MyCustomObject
    def fuzzy?
    true
    end
    end
    !
    describe do
    specify do
    my_model = MyCustomObject.new
    expect(my_model).to be_fuzzy
    end
    end

    View Slide

  16. class MyCustomObject
    def fuzzy?
    true
    end
    end
    !
    describe do
    specify do
    my_model = MyCustomObject.new
    my_model.should be_fuzzy
    end
    end

    View Slide

  17. class MyCustomObject
    def fuzzy?
    true
    end
    end
    !
    describe do
    specify do
    my_model = MyCustomObject.new
    expect(my_model).to be_fuzzy
    end
    end

    View Slide

  18. describe do
    specify do
    expect { fail }.to raise_error
    !
    x = 1
    expect { x += 1 }.to(
    change { x }.from(1).to(2)
    )
    end
    end

    View Slide

  19. EXPECT(YOU).TO CARE

    View Slide

  20. obj.should matcher

    View Slide

  21. expect(obj).to matcher

    View Slide

  22. obj.should_receive

    View Slide

  23. expect(obj).to receive

    View Slide

  24. obj.stub(:msg)

    View Slide

  25. allow(obj).to receive

    View Slide

  26. obj.should_receive(
    some: [vals]
    )

    View Slide

  27. expect(obj).to(
    receive_messages(some: [vals])
    )

    View Slide

  28. DEPRECATED?

    View Slide

  29. View Slide

  30. View Slide

  31. DEPRECATED?

    View Slide

  32. MONKEY PATCHING

    View Slide

  33. obj.should matcher

    View Slide

  34. class FuzzyProxy < BasicObject
    def initialize(target)
    @target = target
    end
    !
    def fuzzy?
    true
    end
    !
    def method_missing(*args, &block)
    @target.__send__(*args, &block)
    end
    end

    View Slide

  35. describe FuzzyProxy do
    !
    it 'is fuzzy' do
    instance = FuzzyProxy.new(:some_object)
    instance.should be_fuzzy
    end
    !
    end

    View Slide

  36. View Slide

  37. REMOVING
    MONKEY PATCHING

    View Slide

  38. View Slide

  39. NO MONKEY PATCHING
    BY DEFAULT

    View Slide

  40. describe "SomeClass" do
    !
    context "in some situation" do
    !
    it "will do something" do
    # ...
    end
    !
    end
    !
    end

    View Slide

  41. RSpec.describe "SomeClass" do
    !
    context "in some situation" do
    !
    it "will do something" do
    # ...
    end
    !
    end
    !
    end

    View Slide

  42. RSpec.configure do |config|

    !
    config.disable_monkey_patching!

    !
    end

    View Slide

  43. FORMATTERS

    View Slide

  44. View Slide

  45. View Slide

  46. MODULARISATION

    View Slide

  47. class CustomFormatter
    # This registers the notifications this formatter
    # supports, and tells
    RSpec::Core::Formatters.register self, :example_sta
    !
    def initialize(output)
    @output = output
    end
    !
    def example_started(notification)
    @output << "example: “
    @output << notification.example.description
    end
    !
    end

    View Slide

  48. PHILOSOPHY

    View Slide

  49. its(:thing) { … }

    View Slide

  50. it { should be … }

    View Slide

  51. it { is_expected.to be … }

    View Slide

  52. its(:thing) { … }

    View Slide

  53. require ‘rspec/its'
    !
    describe do
    !
    person = Struct.new(:first, :last) do
    def name
    [first, last].join(' ')
    end
    end
    !
    subject(:bob) { person.new('Bob', 'Jones') }
    !
    its(:name) { is_expected.to eq 'Bob Jones' }
    !
    end

    View Slide

  54. examples$ rspec lies_spec.rb --format doc
    !
    !
    name
    should eq "Bob Jones"
    !
    Finished in 0.00094 seconds (files took 0.09512
    1 example, 0 failures
    examples$

    View Slide

  55. require ‘rspec/its'
    !
    describe "#name" do
    !
    person = Struct.new(:first, :last) do
    def name
    [first, last].join(' ')
    end
    end
    !
    it "combines first and last" do
    bob = person.new('Bob', 'Jones')
    expect(bob.name).to eq 'Bob Jones'
    end
    !
    end

    View Slide

  56. examples$ rspec refactor_spec.rb --format doc
    !
    !
    name
    combines first and last
    !
    Finished in 0.00084 seconds (files took 0.0084
    1 example, 0 failures
    examples$

    View Slide

  57. VERIFIED DOUBLES

    View Slide

  58. describe "verified doubles" do
    !
    it "does something" do
    my_obj = instance_double(
    "MyObject", does: "cool things”
    )
    expect(my_obj.does).to eq "cool things"
    end
    !
    end

    View Slide

  59. describe "verified doubles" do
    !
    class MyObject
    end
    !
    it "does something" do
    my_obj = instance_double(
    "MyObject", does: "cool things”
    )
    expect(my_obj.does).to eq "cool things"
    end
    end

    View Slide

  60. examples$ rspec verified_doubles_in_situ_spe
    F
    !
    Failures:
    !
    1) verified doubles does something
    Failure/Error: my_obj = instance_double("M
    MyObject does not implement: does
    # ./verified_doubles_in_situ_spec.rb:7:in
    !
    Finished in 0.00046 seconds (files took 0.07
    1 example, 1 failure

    View Slide

  61. describe "verified doubles" do
    !
    class MyObject
    def does; true; end
    end
    !
    it "does something" do
    my_obj = instance_double(
    "MyObject", does: "cool things”
    )
    expect(my_obj.does).to eq "cool things"
    end
    end

    View Slide

  62. describe "verified doubles" do
    !
    class MyObject
    def does; true; end
    end
    !
    it "does something" do
    my_obj = MyObject.new
    allow(my_obj).to receive(:other) { "things" }
    expect(my_obj.other).to eq "things"
    end
    end

    View Slide

  63. examples$ rspec verify_partial_doubles_spec.
    .
    !
    Finished in 0.01582 seconds (files took 0.12
    1 example, 0 failures

    View Slide

  64. describe "verified doubles" do
    !
    class MyObject
    def does; true; end
    end
    !
    RSpec.configuration.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
    end
    !
    it "does something" do
    my_obj = MyObject.new
    allow(my_obj).to receive(:other) { "things" }
    expect(my_obj.other).to eq "things"
    end
    end

    View Slide

  65. examples$ rspec verify_partial_doubles_spec.
    F
    !
    Failures:
    !
    1) verified doubles does something
    Failure/Error: allow(my_obj).to receive(:o
    # does not
    implement: other
    # ./verify_partial_doubles_spec.rb:13:in
    !
    Finished in 0.00562 seconds (files took 0.08
    1 example, 1 failure

    View Slide

  66. CLEAN HOUSE

    View Slide

  67. object.stub!

    View Slide

  68. it { stub “something” }

    View Slide

  69. it { mock “something” }

    View Slide

  70. at_least(0).times

    View Slide

  71. not_to
    raise_error(Specific)

    View Slide

  72. autotest support

    View Slide

  73. textmate formatter

    View Slide

  74. describe do
    let(:thing) { "happen" }
    !
    before(:all) do
    thing
    end
    !
    it { }
    end

    View Slide

  75. examples$ rspec broken_let_spec.rb
    F
    !
    Failures:
    !
    1)
    Failure/Error: thing
    RuntimeError:
    let declaration `thing` accessed in a
    `before(:context)` hook at: examples/brok
    `let` and `subject` declarations are not
    intended to be called in a `before(:conte
    hook, as they exist to define state that
    is reset between each example, while

    View Slide

  76. describe do
    let(:thing) { "happen" }
    !
    before(:all) do
    thing
    end
    !
    it { }
    end

    View Slide

  77. describe do
    !
    before(:all) do
    allow(Object).to receive(:message)
    end
    !
    it { Object.message }
    end

    View Slide

  78. examples$ rspec before_all_stubs_broken_spec
    F
    !
    Failures:
    !
    1)
    Failure/Error: allow(Object).to receive
    The use of doubles or partial doubles
    from rspec-mocks outside of the per-test
    lifecycle is not supported.
    # ./before_all_stubs_broken_spec.rb:4:in
    !
    Finished in 0.00504 seconds (files took 0.07
    1 example, 1 failure

    View Slide

  79. describe do
    !
    before(:all) do
    RSpec::Mocks.with_temporary_scope do
    allow(Object).to receive(:message)
    end
    end
    !
    it { # Object.message }
    end

    View Slide

  80. UPGRADING

    View Slide

  81. TRANSPEC

    View Slide

  82. describe do
    !
    subject(:string) { "a string" }
    !
    it { should be_a String }
    its(:length) { should == 8 }
    !
    specify do
    string.should == "a string"
    end
    !
    end

    View Slide

  83. examples$ transpec
    Gathering the spec suite data...
    !
    Converting examples/upgrade_spec.rb
    !
    Summary:
    !
    2 conversions
    from: it { should ... }
    to: it { is_expected.to ... }
    1 conversion
    from: obj.should
    to: expect(obj).to
    !
    6 conversions, 0 incompletes, 0 warnings, 0

    View Slide

  84. describe do
    !
    subject(:string) { "a string" }
    !
    it { is_expected.to be_a String }
    !
    describe '#length' do
    subject { super().length }
    it { is_expected.to eq(8) }
    end
    !
    specify do
    expect(string).to eq("a string")
    end
    !
    end

    View Slide

  85. AFTER(:TALK)

    View Slide

  86. THANKS

    View Slide

  87. View Slide

  88. THANKS
    @JONROWE
    HTTP://JONROWE.CO.UK

    View Slide