41

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 
4

8 に答える 8

78

私はこの構文を好みます (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構文エラーを避けるために、後ろにバックスラッシュを追加しました

于 2014-05-10T15:33:27.377 に答える
23

@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 を使用していることに言及する必要があります。このバージョンの解析に微妙な変更があり、エラーが発生するかどうかはわかりません。このスレッドの他の誰かがその問題を抱えているようには見えません。

于 2015-07-24T23:31:56.717 に答える
20

これは 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
于 2012-11-29T00:09:40.680 に答える
11

以前に提案された簡略化/コンテキストベースのアプローチを使用したくない場合は、次のようなこともできますが、期待値が 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
于 2014-01-06T18:45:10.277 に答える
4

Georg Ladermann の構文はより優れていますが、機能しません。複数の値の変更をテストする方法は、配列内の値を結合することです。それ以外の場合、最後の変更アサーションのみがテストを決定します。

これが私がそれを行う方法です:

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

これは、「.to」関数と完全に連携します。

于 2014-05-30T15:27:53.433 に答える
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)

最もエレガントなソリューションではありませんが、機能します

于 2015-02-22T17:49:29.167 に答える
3

提案されたソリューションがどれも実際に機能しないことが証明された後、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動作させるには、追加の作業が必要になります。

于 2014-07-06T00:59:26.450 に答える