RSpec 3 的計畫
Myron Marston
2013 年 7 月 15 日更新:現在有這個的日文翻譯。
RSpec 2.0 於 2010 年 10 月發布。在那之後的將近三年裡,我們不斷改進 RSpec,而無需進行破壞性變更,但現在我們發現 RSpec 有相當多的雜亂,起因於需要保持與舊版 2.x 發行版本的向後相容性。
RSpec 2.14 將會是最後一個 RSpec 2 的功能版本。(不過我們可能會做一些錯誤修正的修補程式發行)。我們正開始著手開發 RSpec 3,我想分享我們對 RSpec 未來發展方向的想法。
當然,這些都不是鐵板釘釘的,而且最終 RSpec 之所以能成為一個成功的專案,都要歸功於所有使用它的人。所以如果您對我們應該如何開發 RSpec 3 有任何想法,請暢所欲言!
將要移除的內容
不再支援 1.8.6 和 1.9.1
在 MRI 團隊不再支援 Ruby 1.8.6 之後,RSpec 2.x 仍然繼續支援它。作為 Ruby 生態系統中一個重要的測試基礎設施,我們認為讓 gem 作者決定何時停止支援舊版 Ruby 版本很重要,而不是因為他們選擇使用的測試框架不再支援他們支援的版本而被迫過早停止支援。
Ruby 1.8.6 和 1.9.1 已在 Travis 上將近兩年無法使用,而且在沒有 CI 伺服器在這些版本上執行建置的安全網下,繼續支援它們變得極為困難。實際上,在過去幾年裡,我們真的只是「半支援」這些 Ruby 版本:當使用者在這些 Ruby 版本上回報問題時,我們會修復它們,但我們沒有投入額外的精力來支援它們。
因此,現在是時候停止支援這些版本了。我們計畫在 RSpec 3 上繼續支援 1.8.7、1.9.2 和所有較新的 Ruby 版本。鑑於 1.8.7 現在是舊版,我們可能會在 RSpec 4 中停止支援 1.8.7,不過如果/當 Travis 在那之前停止支援它時,我們將只能像我們半支援 1.8.6 那樣「半支援」它。
核心:its
將會被移到外部 gem 中
我之前已經寫過這個,所以這裡我不再贅述。我們計畫將 its
從 rspec-core 中移到外部 gem 中。
期望:have(x).items
比對器將會被移到外部 gem 中
RSpec 源自於 Cucumber/Gherkin 存在之前的時代,它的早期目標之一是以專案利害關係人可以理解的自然語言來表達事物。在那些早期,像 team.should have(9).players
這樣的表達方式對於專案的目標來說是合理的。自那時起,Cucumber/Gherkin 已成為以利害關係人為中心的測試的更好選擇,而 RSpec 今天很少用於該目的。當像 expect(team.players.size).to eq(9)
這樣的簡單表達方式就可以很好地工作時,have(x).items
系列的比對器(包括 have_at_least(x).items
和 have_at_most(x).items
同級比對器)不必要地複雜。
我們計畫將這些比對器從 rspec-expectations
中移到外部 gem 中。
核心:不再明確支援偵錯器
長期以來,RSpec 一直支援 -d
/ --debug
命令行選項,以便透過 ruby-debug
gem 啟用偵錯器。然而,現在 ruby-debug
並不是當今唯一(甚至主要)使用的偵錯 gem。debugger 已成為 MRI 1.9.2+ 事實上的標準偵錯 gem,而且許多開發人員更喜歡使用 pry 來偵錯。其他 Ruby 直譯器(如 Rubinius)具有 它們自己的偵錯器。
我們計畫在 RSpec 3 中移除明確的偵錯器支援。除了移除命令行選項外,我們還將移除當未載入 ruby-debug 時 在 Kernel 中對偵錯器的猴子補丁,因此當未載入偵錯器時,您將從 debugger
取得 NoMethodError
。
如果您想繼續使用命令行選項載入偵錯器,您可以使用 require 旗標 (-r
),使用像 -rdebugger
這樣的選項。
核心:不再整合 RCov
RSpec::Core::RakeTask
長期以來一直有一些 RCov 選項。RCov 僅適用於 MRI 1.8,而且現在大多數 Ruby 開發人員使用 SimpleCov 來滿足其程式碼涵蓋範圍需求。SimpleCov 可以非常簡單地與 RSpec(或任何測試框架)整合,而無需 RSpec 本身內部的明確支援。
核心:Autotest 整合將會被移到外部 gem 中
Autotest 曾經是主要的 Ruby 持續測試執行器。現在,guard 似乎是更受歡迎的選擇,而且 RSpec 的 Autotest 整合沒有理由留在 rspec-core 中。
核心:TextMate 格式器將會被移到 TextMate 套件中
多年來,TextMate 一直是 Ruby 開發人員最常用的文字編輯器。多年來,RSpec 一直有一個 TextMate 專用的格式器。現在,TextMate 在 Ruby 開發人員中的受歡迎程度遠不如以前,而且 TextMate 格式器沒有令人信服的理由留在 rspec-core 中。
許多棄用
RSpec 2.14 包含許多在過去幾年中已被棄用的東西。我們計畫移除幾乎所有已棄用的 API 和功能。
舊的期望/模擬語法呢?
RSpec 2.11 引入了一種新的基於 expect
的 rspec-expectations 語法。在 RSpec 2.14 中,我們更新了 rspec-mocks 以使用類似的語法。自引入新語法以來,我收到了一些關於我們將會在多久之後棄用或移除舊的基於 should
的語法的問題。
雖然我不會說「永遠不會」(誰知道未來會發生什麼?),但我們目前沒有計畫要移除舊的語法。使用者多年來一直在使用舊語法的程式碼上投入心力,雖然我們建議使用新語法(尤其是對於新專案),但如果我們很快移除舊語法,將會對使用者造成傷害。它也不是一個重大的維護負擔。
對於 RSpec 3,我們考慮了預設停用舊語法,強制使用者選擇使用它的想法。不過,我認為這樣做會對透過不夠新的教學進入 RSpec 的新使用者造成傷害。對於第一次嘗試 RSpec 的人來說,從教學複製的範例得到 NoMethodError
可能會非常令人沮喪。有經驗的使用者可以輕鬆停用舊語法,而新使用者可能沒有足夠的 RSpec 知識來知道啟用其教學使用的舊語法。
也就是說,我們確實希望鼓勵人們切換到新語法,因此我們計畫讓 RSpec 3 在首次使用任何舊語法方法(should
、should_not
、should_receive
等)時列印警告,除非已明確啟用 should
語法。這應該會引導人們轉向新語法,同時保持 RSpec 對新使用者的友善度,並為 RSpec 4 中預設停用舊語法鋪平道路。
新內容
零猴子補丁模式!
在歷史上,RSpec 廣泛使用猴子補丁來建立其可讀的語法,將 describe
、shared_examples_for
、shared_context
、should
、should_not
、should_receive
、should_not_receive
和 stub
等方法新增到每個物件。在過去的幾個 2.x 版本中,我們致力於減少 RSpec 所做的猴子補丁量
- 從 rspec-core 2.11 開始,
describe
不再新增到每個物件。相反地,它只會新增到最上層的main
物件和Module
(以便它可以從類別和模組內使用)。 - 在 rspec-expectations 2.11 中,我們新增了
expect
語法,並提供了一個組態選項來停用should
語法 - 這會從每個物件中移除should
和should_not
。 - 從 rspec-core 2.12 開始,
shared_examples_for
和shared_context
不再新增到每個物件。與describe
一樣,它們只會新增到最上層的main
物件和Module
。 - 在 rspec-mocks 2.14 中,我們更新了
rspec-mocks
以支援基於expect
的語法,並提供了一個組態選項來停用舊的模擬語法 - 這會從每個物件中移除should_receive
、should_not_receive
和stub
。
如上所述,我們將在 3.0 中移除 RSpec 的猴子補丁 Kernel#debugger
。我們還計畫提供一個組態選項來移除將頂層 DSL 方法(describe
、shared_examples_for
等)猴子補丁到 main
和 Module
上,而是要求您在這些方法前面加上 RSpec.
RSpec.describe MyClass do
# Within an example group you'll still be able to use
# a bare `describe`:
describe "#some_method" do
end
# And you'll be able to use a bare `shared_examples_for`:
shared_examples_for "something" do
end
end
RSpec.shared_examples_for "some behavior" do
end
最終的結果將會是一組組態選項(一個用於 rspec-expectations
,一個用於 rspec-mocks
,一個用於 rspec-core
),它將為 RSpec 提供零猴子補丁模式。(我們也可能會提供一個設定所有三者的單一統一組態選項)。
我們計畫讓這些組態選項成為 RSpec 4.0 中的預設值,以便 RSpec 4.0 將具有開箱即用的零猴子補丁。
模擬:測試雙重介面驗證
不幸的是,測試雙重與它們正在雙重的真實介面失去同步非常容易。當您重新命名方法,或變更方法預期的參數數量時,很容易忘記更新您正在使用作為已變更類別替身的測試雙重。
我一直很喜歡 rspec-fire 解決這個問題的方法。我計畫將它的一個版本移植到 rspec-mocks。
請查看我們正在討論此問題的 github 議題 以取得完整詳細資料(此功能的 API 和語意當然尚未確定,因此請在該票證上發表您的想法!)。
期望:完全可組合的比對器
在 RSpec 2.13 中,我們新增了支援,讓 include
比對器可以接受一個比對器列表來進行比對。這種可組合性非常有用,我們計畫在 RSpec 3 中將其擴展到所有比對器。例如,您可以使用像這樣的表達式
expect { |b|
some_object.do_something(&b)
}.to yield_with_args(include(match(/foo/), match(/bar/)))
這表達了一個詳細的期望:「我期望 some_object.do_something
會產生一個包含符合 /foo/
的字串和符合 /bar/
的字串的集合。」
我們也在考慮新增匹配器別名,讓以這種方式組合時讀起來更好,這樣你就可以寫成這樣:
expect { |b|
some_object.do_something(&b)
}.to yield_with_args(a_collection_including(a_string_matching(/foo/),
a_string_matching(/bar/)))
如需更多詳細資訊,或想針對此議題發表意見,請查看 github issue。
核心:格式化器 API 的改進
目前用於通知格式化器測試套件進度的 API 在新增通知和變更現有通知方面已證實有些不夠彈性。我們計劃從幾個方面變更它:
- 格式化器不再需要實作所有通知方法,而是可以訂閱特定的通知。這將允許您實作您的格式化器所需的最少方法集,並且讓我們可以新增新通知,而無需擔心會破壞現有的格式化器。
- 通知參數將從有序的參數列表變更為單一的值物件,這將允許我們輕鬆地將額外資料新增至特定通知,而無需變更方法簽名。
這些變更將允許我們進行在 2.x 版本中無法進行的進一步改進。我們還計劃在 RSpec 3 中提供一個相容層,封裝針對舊 API 撰寫的格式化器,並使它們適應新的 API,以便當使用者依賴舊格式化器時,可以更輕鬆地升級。
如需更多詳細資訊,請查看 github issue。
核心:DSL 方法將產生範例
在 RSpec 2 中,目前執行的範例會以 example
的形式公開。它可以用於存取範例的中繼資料。當使用者不經意地定義自己的 example
方法時,這偶爾會造成問題。在 RSpec 3 中,我們將移除 example
方法,選擇從每個在範例上下文中執行的 DSL 方法產生範例。
describe MyClass do
before(:each) { |example| }
subject { |ex| }
let(:user) { |ex| User.find(ex.metadata[:user_id]) }
# before(:all) will NOT yield an example
it "can access the current example using a block local" do |example|
# do something with `example`
end
end
我們知道這可能會對依賴使用 example
API 的 gem 的使用者(例如 Capybara)造成升級上的困擾。我們正在討論使升級更順暢的方法,無論是對 gem 使用者還是作者。如需更多資訊,請參閱 github issue。
期望:匹配器協定和自訂匹配器 API 變更
雖然 RSpec 一直在擺脫其基於 should
的語法,但匹配器協定和自訂匹配器 API 並未相應地變更。匹配器協定仍然依賴諸如 failure_message_for_should
和 failure_message_for_should_not
之類的方法,而自訂匹配器 API 具有諸如 match_for_should
和 match_for_should_not
之類的方法。
在 RSpec 3 中,我們希望變更匹配器協定和自訂匹配器 API,使其不再以 should
的術語表示,同時保留向後相容層,以便現有的匹配器可以繼續工作,並計劃在 RSpec 4 中移除該相容層。我們尚未確定新的 API 會是什麼樣子;如果您有任何想法,請在 github issue 上提出您的看法。
模擬:any_instance
區塊實作將產生接收器
當使用 any_instance
存根方法時,您可以像正常的存根一樣傳遞區塊實作。但是,如果您想在區塊中存取接收器(即接收訊息的實例),則無法實現此目標。在 RSpec 3 中,我們正在修正此疏忽,並且接收器將作為第一個區塊參數產生。
allow_any_instance_of(User).to receive(:age) do |user|
((Date.today - user.birthdate) / 365).floor
end
為了向後相容性,我們將新增一個組態選項來停用此行為。
升級路徑
儘管 RSpec 3.0 將是一個主要版本,允許我們自 2010 年以來首次進行有意的重大變更,但對於我們來說,現有測試套件的升級路徑應盡可能簡單是很重要的。為此,我們計劃發布 2.99 版本,該版本將純粹用於協助使用者升級。以下是我們心中的想法:
- RSpec 2.99 將是 RSpec 2.14,加上一些針對 3.0 中將被移除的內容的額外棄用警告。
- 您應該能夠取得現有的 RSpec 2.x 測試套件並升級到 2.99,而無需進行任何變更。
- RSpec 2.99 將為我們在 3.0 中移除或以某種方式破壞的任何內容提供棄用通知。您應該解決這些警告。一旦您在 2.99 上沒有警告,您應該能夠升級到 3.0,而無需進行任何進一步的變更。
2.99 版本將是升級過程中不應跳過的重要步驟。它將為您提供一個專門為您的測試套件對 RSpec 的使用量身定制的升級檢查表,為您提供比瀏覽變更記錄並試圖找出 RSpec 3 中的所有變更更簡單、更有效率的升級方式。
開發和發布計畫
我們已經開始在每個 RSpec 儲存庫的主分支中處理 RSpec 3。我們還有一個 2-14-維護分支用於 2.14 變更(即可能發布的修補程式版本)和一個 2-99-維護分支用於 2.99 中的變更。我們計劃在朝向最終 3.0 版本邁進時進行多次候選發行(以及可能的一些 Beta 版本)。
我不會猜測我們何時可能會發布 RSpec 3。經驗告訴我,軟體發布日期估計總是錯誤的 :(。
「我如何提供協助?」
目前的 RSpec 核心團隊(David、Andy、Jon、Sam、Bradley 和 我自己)將推動 3.0 版本的相關工作…但我們始終樂於獲得社群的協助。以下是一些您可以提供協助的特定方式:
- 如上所述,我們計劃將 RSpec 的某些功能提取到外部 gem(
its
、have
匹配器和自動測試整合)。如果能找到社群中可以挺身而出並接手這些 gem 的維護(甚至可以進行初始提取,如果您有此意願)的人士,那就太好了。 - 我們重視社群對 RSpec 發展方向的回饋,因此如果您有任何想法或意見,請在上述我連結的議題(以及任何其他議題,事實上 – RSpec 3 中的議題將比我在此涵蓋的更多!)上發表評論。
- 如果您想協助為 RSpec 貢獻程式碼,那就太好了!但是,在此期間,各種 RSpec gem 的程式碼庫中的變動將比平常更多,導致與提取請求的合併衝突更多,並且讓我們更難整合使用者的貢獻。因此,如果您想做出貢獻,請在對 PR 投入大量精力之前先聯繫我們(透過在 github 議題上發表評論和/或在 irc.freenode.net 上的 #rspec 頻道與我們聊天)。預先進行一些溝通將有助於我們輕鬆整合您的貢獻。
- 您可以提供的最重要的協助方式之一是試用 2.99 和 3.0 的預發行版本,並向我們提供回饋。請關注發布公告。
- rspec.info 非常需要更新,如果能夠與 RSpec 3 的發布同步推出該網站的新版本,那就太好了。但是,RSpec 核心團隊很難找到時間來處理網站,而且我們大多數人更專注於後端而不是前端開發。如果有一些社群成員能夠挺身而出並在此領域提供協助,那就太好了!