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
答案 0 :(得分:75)
我更喜欢这种语法(rspec 3或更高版本):
it "should increment the counters" do
expect { Foo.bar }.to change { Counter, :count }.by(1).and \
change { AnotherCounter, :count }.by(1)
end
是的,这是一个地方的两个断言,但由于块只执行了一次,它可以加速测试。
编辑:在.and
之后添加反斜杠以避免语法错误
答案 1 :(得分:22)
我在尝试使用@MichaelJohnston's solution时出现语法错误;这是最终为我工作的形式:
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 - 我不知道这个版本在解析中是否有一些微妙的变化导致我出错,它并不会出现在其他人身上在这个帖子中遇到了这个问题。
答案 2 :(得分:20)
这应该是两个测试。 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
答案 3 :(得分:11)
如果您不想使用之前建议的基于速记/上下文的方法,您也可以执行类似的操作,但要注意它会运行两次预期,因此可能不适合所有测试。
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
答案 4 :(得分:4)
我发现的最好方法是“手动”:
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)
不是最优雅的解决方案,但它有效
答案 5 :(得分:3)
Georg Ladermann的语法更好,但它不起作用。测试多个值更改的方法是组合数组中的值。否则,只有最后一次更改断言将决定测试。
我是这样做的:
it "should increment the counters" do
expect { Foo.bar }.to change { [Counter.count, AnotherCounter.count] }.by([1,1])
end
这完全适用于' .to'功能
答案 6 :(得分:2)
我忽略了最佳做法有两个原因:
我这样做的方式(当我需要这样做时)是依赖于我的数据库开始为空的事实,所以我可以写:
foo.bar
expect(Counter.count).to eq(1)
expect(Anothercounter.count).to eq(1)
在某些情况下,我的数据库不是空的,但我要么知道之前的计数,要么我可以明确地测试之前的计数:
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
如果你对我有类似的需求,那么我的唯一建议就是睁大眼睛这样做 - 其他提出的解决方案更优雅,但在某些情况下会有一些缺点。
答案 7 :(得分:2)
在提出的解决方案都没有得到证实后,我通过添加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_least
和by_at_most
为change_multiple工作需要一些额外的工作。