RSpec是否有可能期望两个表格发生变化?

RSpec期望改变:

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

有两种方法可以期待两个表格的变化吗?

 expect{Foo.bar}.to change{Counter.count}.by 1 and change{AnotherCounter.count}.by 1 

这应该是两个testing。 RSpec最佳实践要求每个testing一个断言 。

 describe "#bar" do subject { lambda { Foo.bar } } it { should change { Counter.count }.by 1 } it { should change { AnotherCounter.count }.by 1 } end 

我更喜欢这个语法(rspec 3或更高版本):

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

是的,这是一个地方的两个断言,但是因为这个块只执行一次,它可以加速testing。

编辑:。 .and以避免语法错误后添加反斜杠

我有语法错误试图使用@ MichaelJohnston的解决scheme ; 这是最终为我工作的forms:

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

我应该提到我正在使用ruby 2.2.2p95 – 我不知道这个版本是否有一些细微的变化,导致我得到错误的parsing,这似乎并没有在这个线程中的任何人有这个问题。

如果你不想使用前面提到的基于速记/上下文的方法,你也可以做这样的事情,但要警告它会两次运行期望,所以它可能不适合所有的testing。

 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 

我发现的最好的方法是“手动”

 counters_before = Counter.count another_counters_before = AnotherCounter.count Foo.bar expect(Counter.count).to eq (counters_before + 1) expect(AnotherCounter.count).to eq (another_counters_before + 1) 

不是最优雅的解决scheme,但它的作品

Georg Ladermann的语法更好,但不起作用。 testing多个值更改的方法是通过组合数组中的值。 否则,只有最后一个更改断言才会决定testing。

下面是我如何做到这一点:

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

这与“.to”function完美结合。

我忽略了最佳实践,原因有两个:

  1. 我的一套testing是回归testing,我希望它们跑得快,而且很less破坏。 明确什么是破解的好处并不是很大,重构我的代码的速度放缓,以便它多次运行相同的事件对我来说是重要的。
  2. 有时候我有点懒,更不容易做重构

我这样做(当我需要这样做)的方式是依靠我的数据库开始空的事实,所以我可以写:

 foo.bar expect(Counter.count).to eq(1) expect(Anothercounter.count).to eq(1) 

在某些情况下,我的数据库不是空的,但是我知道之前计数,或者我可以显式地testing之前计数:

 counter_before = Counter.count another_counter_before = Anothercounter.count foo.bar expect(Counter.count - counter_before).to eq(1) expect(Anothercounter.count - another_counter_before).to eq(1) 

最后,如果你有很多对象需要检查(我有时会这么做),你可以这样做:

 before_counts = {} [Counter, Anothercounter].each do |classname| before_counts[classname.name] = classname.count end foo.bar [Counter, Anothercounter].each do |classname| expect(classname.count - before_counts[classname.name]).to be > 0 end 

如果你对我有类似的需求,那么我的唯一的build议就是用你的眼睛来做这个事情 – 其他的解决scheme更加优雅,但是在某些情况下有一些缺点。

在所提出的解决scheme都没有被certificate实际工作之后,我通过添加一个change_multiple匹配器来实现这一点。 这将只适用于RSpec 3,而不是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 

用法示例:

 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 

使by_at_leastby_at_most为change_multiple工作将需要一些额外的工作。