[英]Multiple assertions for single setup in RSpec
我有一些較慢的規格,我想優化。 這種規范的例子如下:
require 'rspec'
class HeavyComputation
def compute_result
sleep 1 # something compute heavy here
"very big string"
end
end
describe HeavyComputation, 'preferred style, but slow' do
subject { described_class.new.compute_result }
it { should include 'big' }
it { should match 'string' }
it { should match /very/ }
# +50 others
end
這是非常易讀的,我對它很滿意,除了每個額外的規范都會在總運行時間內增加至少1秒。 這是不可接受的。
(請不要討論HeavyComputation
類的優化,因為它超出了本問題的范圍)。
所以我要求的是這樣的規格:
describe HeavyComputation, 'faster, but ugly' do
subject { described_class.new.compute_result }
it 'should have expected result overall' do
should include 'big'
should match 'string'
should match /very/
# +50 others
end
end
這顯然是更好的性能,因為運行它的時間總是幾乎不變。 問題是失敗很難追查,閱讀起來不是很直觀。
理想情況下,我希望兩者兼而有之。 這些方面的東西:
describe HeavyComputation, 'what I want ideally' do
with_shared_setup_or_subject_or_something_similar_with do
shared(:result) { described_class.new.compute_result }
subject { result }
it { should include 'big' }
it { should match 'string' }
it { should match /very/ }
# +50 others
end
end
但不幸的是,我無法看到哪里開始實施它。 它有許多潛在的問題(如果在共享結果上調用鈎子的話)。
如果現有解決方案存在問題我想知道什么。 如果不是,那么解決它的最佳方法是什么?
您可以使用before(:context)
鈎子來實現此目的:
describe HeavyComputation, 'what I want ideally' do
before(:context) { @result = described_class.new.compute_result }
subject { @result }
it { should include 'big' }
it { should match 'string' }
it { should match /very/ }
# +50 others
end
請注意, before(:context)
會有許多警告:
before(:context)
使用before(:context)
來加快速度是非常誘人的,但是我們建議你避免這種情況,因為有很多陷阱,以及根本不起作用的東西。
before(:context)
在生成的示例中運行,以提供塊的組上下文。
在before(:context)
中聲明的實例變量在組中的所有示例之間共享。 這意味着每個示例都可以更改共享對象的狀態,從而導致排序依賴性,這使得很難推斷出失敗。
RSpec有幾個構造可以自動重置每個示例之間的狀態。 這些不適合在before(:context)
:
let
聲明 subject
聲明 模擬對象框架和數據庫事務管理器(如ActiveRecord)通常圍繞在示例之前設置,運行該示例,然后拆除的想法而設計。 這意味着模擬和存根可以(有時)在before(:context)
聲明,但在第一個真實示例運行之前就會被拆除。
您可以在rspec-rails中的before(:context)
中創建數據庫支持的模型對象,但它不會包含在事務中,因此您可以自行清理after(:context)
塊。
(來自http://rubydoc.info/gems/rspec-core/RSpec/Core/Hooks:before )
只要您了解您的before(:context)
掛鈎超出了測試雙打和數據庫事務等正常的每個示例生命周期,並且管理必要的設置並明確地自行拆除,那么你會很好 - 但是其他人你的代碼庫上的工作可能不知道這些陷阱。
在版本3.3中添加的aggregate_failures
將執行您要求的一些內容。 它允許您在規范中有多個期望,RSpec將運行每個並報告所有故障,而不是在第一個故障停止。
問題在於,由於您必須將其放在單個規范中,因此您無需為每個期望命名。
有一個塊形式:
it 'succeeds' do
aggregate_failures "testing response" do
expect(response.status).to eq(200)
expect(response.body).to eq('{"msg":"success"}')
end
end
和元數據表單,適用於整個規范:
it 'succeeds', :aggregate_failures do
expect(response.status).to eq(200)
expect(response.body).to eq('{"msg":"success"}')
end
請參閱: https : //www.relishapp.com/rspec/rspec-core/docs/expectation-framework-integration/aggregating-failures
@Myron Marston給了一些靈感,所以我第一次嘗試以或多或少的可重用方式實現它最終得到以下用法(注意shared_subject
):
describe HeavyComputation do
shared_subject { described_class.new.compute_result }
it { should include 'big' }
it { should match 'string' }
it { should match /very/ }
# +50 others
end
我們的想法是只在第一個規范上而不是在共享塊中渲染主題一次。 它幾乎沒有必要改變任何東西(因為所有鈎子都將被執行)。
當然, shared_subject
假設共享狀態及其所有怪癖。
但是每個新的嵌套context
都會創建一個新的共享主題,並在某種程度上消除了狀態泄漏的可能性。
更重要的是,我們需要做的就是處理狀態泄漏(如果這些泄漏)是將shared_subject
替換回subject
。 然后你運行正常的RSpec示例。
我確信實現有一些怪癖,但應該是一個非常好的開始。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.