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
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
這應該是兩個測試。 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
我忽略了最佳做法的原因有兩個:
的方式我這樣做(當我必須這樣做)是依靠事實證明我的數據庫啓動空的,所以我可以接着寫:
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)
最後LY,如果你有大量的對象,檢查(我有時做),你可以這樣做,因爲:
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
如果您有類似的需求對我來說這將工作,我唯一的建議是用做這個你睜着眼睛 - 提出的其他解決方案更加優雅,但在某些情況下有一些缺點。
如果您不想使用前面建議的基於速記/上下文的方法,您也可以這樣做,但會被警告它會運行兩次期望值,因此可能不適合所有測試。
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
我喜歡這種語法(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
後,以避免語法錯誤
這隻測試最後的斷言,而忽略所有以前的斷言。請參閱我的回答,瞭解合併斷言的正確方法。 – somecto
在Rspec 3中,如果使用組合運算符'.and'(但不能像在原始答案中那樣使用rspec文檔或&&運算符中描述的單個'&'別名),則上述方法將起作用。我編輯了答案,按預期工作。 –
實際上,使用rspec 3的組合結果在塊運行多次:(嘆氣。謝謝貨物Cultists堅持譴責每個人的集成測試,需要更長的數量級運行比他們需要。 –
喬治Ladermann的語法是更好,但它不工作。測試多個值更改的方法是通過組合數組中的值。否則,只有最後一次更改斷言纔會決定測試。
這是我如何做到這一點:
it "should increment the counters" do
expect { Foo.bar }.to change { [Counter.count, AnotherCounter.count] }.by([1,1])
end
這與 '爲了' 功能工作perfectecly。
不幸的是,這不是在做你認爲的事情。它只是斷言最後的計數是[1,1]。爲了明白我的意思,用一些已經存在的Counter記錄運行你的測試。 –
請參閱[我的答案](http://stackoverflow.com/a/24591809/173542)爲rspec 3工作change_multiple匹配器 –
@MichaelJohnston你說得很對。它不適用'.by'方法,但它可以與'.to'方法一起使用。所以實際上,如果可以預期多個表中的變化,它確實回答了原來的問題。另外,在這種情況下塊只運行一次。我同意將測試限制在一個元素上實在是不理想。 – somecto
在所有提出的解決方案都未被證明實際工作之後,我通過添加一個change_multiple
匹配器來完成此任務。這將僅適用於3 RSpec的工作,使用的不是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_multiple by_at_least
和by_at_most
工作將需要一些額外的工作。
這很好,但是混合斷言是不可能的,例如'x'應該從1改變爲2,'y'應該改變爲3,'z'應該從4改變,並且k應該簡單地改變。 –
我發現最好的辦法是做「人工」:
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)
不是最完美的解決方案,但它的工作原理
我試圖用@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
我要提到我使用紅寶石2.2.2p95 - 我不知道,如果這個版本在解析一些微妙的變化,導致我得到的錯誤,似乎沒有任何其他人在這個線程中有這個問題。
是的。這也適用於我。謝謝。 – poweratom
這裏另一個大拇指。在2016年中期的最新版本中工作。你也可以將params而不是block傳遞給像這樣的'change'方法:'如果這會讓你感到尷尬,請改變(Counter,:count).by(1)'。 –
有時這會導致大量樣板代碼(當規範需要複雜的設置時)。或者,也許我只是做錯了:) –
一般來說,如果我做了很多樣板,我會嘗試改進我的上下文/描述塊,以便我可以在之前的塊中進行設置。這通常會清理它。 –
對於模型/單元測試來說,這一切都很好,但是關於特性/集成測試呢,在一次測試中做出許多斷言是正常的呢? – Arcolye