Is it possible for RSpec to expect change in two tables?

后端 未结 8 1548
借酒劲吻你
借酒劲吻你 2020-12-09 07:15

RSpec expect change:

it \"should increment the count\" do
  expect{Foo.bar}.to change{Counter.count}.by 1
end

Is there a way to expect chan

相关标签:
8条回答
  • 2020-12-09 07:58

    I got syntax errors trying to use @MichaelJohnston's solution; this is the form that finally worked for me:

    it "should increment the counters" do
      expect { Foo.bar }.to change { Counter.count }.by(1)
        .and change { AnotherCounter.count }.by(1)
    end
    

    I should mention I'm using ruby 2.2.2p95 - I don't know if this version has some subtle change in parsing that causes me to get errors, it doesn't appear that anyone else in this thread has had that problem.

    0 讨论(0)
  • 2020-12-09 07:59

    If you don't want to use the shorthand/context based approach suggested earlier, you can also do something like this but be warned it will run the expectation twice so it might not be appropriate for all tests.

    it "should increment the count" do
      expectation = expect { Foo.bar }
      expectation.to change { Counter.count }.by 1
      expectation.to change { AnotherCounter.count }.by 1
    end
    
    0 讨论(0)
  • 2020-12-09 08:02

    This should be two tests. RSpec best practices call for one assertion per test.

    describe "#bar" do
      subject { lambda { Foo.bar } }
    
      it { should change { Counter.count }.by 1 }
      it { should change { AnotherCounter.count }.by 1 }
    end
    
    0 讨论(0)
  • 2020-12-09 08:03

    Georg Ladermann's syntax is nicer but it doesn't work. The way to test for multiple value changes is by combining the values in arrays. Else, only the last change assertion will decide on the test.

    Here is how I do it:

    it "should increment the counters" do
      expect { Foo.bar }.to change { [Counter.count, AnotherCounter.count] }.by([1,1])
    end
    

    This works perfectecly with the '.to' function.

    0 讨论(0)
  • 2020-12-09 08:05

    After none of the proposed solutions proved to actually work, I accomplished this by adding a change_multiple matcher. This will only work for RSpec 3, and not 2.*

    module RSpec
      module Matchers
        def change_multiple(receiver=nil, message=nil, &block)
          BuiltIn::ChangeMultiple.new(receiver, message, &block)
        end
        alias_matcher :a_block_changing_multiple,  :change_multiple
        alias_matcher :changing_multiple,          :change_multiple
    
        module BuiltIn
          class ChangeMultiple < Change
            private
    
              def initialize(receiver=nil, message=nil, &block)
                @change_details = ChangeMultipleDetails.new(receiver, message, &block)
              end
          end
          class ChangeMultipleDetails < ChangeDetails
            def actual_delta
              @actual_after = [@actual_after].flatten
              @actual_before = [@actual_before].flatten
              @actual_after.map.with_index{|v, i| v - @actual_before[i]}
            end
          end
        end
      end
    end
    

    example of usage:

    it "expects multiple changes despite hordes of cargo cultists chanting aphorisms" do
      a = "." * 4
      b = "." * 10
      times_called = 0
      expect {
        times_called += 1
        a += ".."
        b += "-----"
      }.to change_multiple{[a.length, b.length]}.by([2,5])
      expect(times_called).to eq(1)
    end
    

    Making by_at_least and by_at_most work for change_multiple would require some additional work.

    0 讨论(0)
  • 2020-12-09 08:09

    I prefer this syntax (rspec 3 or later):

    it "should increment the counters" do
      expect { Foo.bar }.to change { Counter,        :count }.by(1).and \
                            change { AnotherCounter, :count }.by(1)
    end
    

    Yes, this are two assertions in one place, but because the block is executed just one time, it can speedup the tests.

    EDIT: Added Backslash after the .and to avoid syntax error

    0 讨论(0)
提交回复
热议问题