RSpecは変更を期待しています:
it "should increment the count" do
expect{Foo.bar}.to change{Counter.count}.by 1
end
2 つのテーブルで変更を期待する方法はありますか?
expect{Foo.bar}.to change{Counter.count}.by 1
and change{AnotherCounter.count}.by 1
私はこの構文を好みます (rspec 3 以降):
it "should increment the counters" do
expect { Foo.bar }.to change { Counter, :count }.by(1).and \
change { AnotherCounter, :count }.by(1)
end
はい、これは 1 か所で 2 つのアサーションですが、ブロックが 1 回だけ実行されるため、テストを高速化できます。
編集:.and
構文エラーを避けるために、後ろにバックスラッシュを追加しました
@MichaelJohnston のソリューションを使用しようとすると、構文エラーが発生しました。これは最終的に私のために働いたフォームです:
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 つのテストである必要があります。RSpec のベスト プラクティスでは、テストごとに 1 つのアサーションが必要です。
describe "#bar" do
subject { lambda { Foo.bar } }
it { should change { Counter.count }.by 1 }
it { should change { AnotherCounter.count }.by 1 }
end
以前に提案された簡略化/コンテキストベースのアプローチを使用したくない場合は、次のようなこともできますが、期待値が 2 回実行されるため、すべてのテストに適していない可能性があることに注意してください。
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
Georg Ladermann の構文はより優れていますが、機能しません。複数の値の変更をテストする方法は、配列内の値を結合することです。それ以外の場合、最後の変更アサーションのみがテストを決定します。
これが私がそれを行う方法です:
it "should increment the counters" do
expect { Foo.bar }.to change { [Counter.count, AnotherCounter.count] }.by([1,1])
end
これは、「.to」関数と完全に連携します。
私が見つけた最良の方法は、「手動で」行うことです。
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)
最もエレガントなソリューションではありませんが、機能します
提案されたソリューションがどれも実際に機能しないことが証明された後、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
change_multipleby_at_least
を作成してby_at_most
動作させるには、追加の作業が必要になります。