蒼時弦也
蒼時弦也
資深軟體工程師
發表於

優雅的 RSpec 測試 - 常見匹配器

這篇文章是 優雅的 RSpec 測試 系列的一部分。

經過測試案例、組織測試、前置處理等等概念後,我們現在已經掌握了對於如何規劃一個「容易理解」的測試有所概念,接下來我們要針對 RSpec 的斷言機制中,強大的「匹配器(Matcher)」來深入討論。

Equal

Equal(相等)是最為常見的匹配器,在其他測試框架中也可以算是標準配備,在 RSpec 中我們可以使用 eq 的縮寫來使用這個匹配器。

1RSpec.describe Calculator do
2  subject(:calculator) { Calculator.new }
3
4  describe '#add' do
5    subject { calculator.add(1, 1) }
6
7    it { is_expected.to eq(2) }
8  end
9end

以上面的例子來說,我們呼叫了 Calculator#add 這個方法,並且預期回傳的數值是 2 就可以使用 eq 來檢查。

假設是在其他測試框架,通常會使用 assert(斷言)關鍵字,並且直接確認是否是 true 來進行判斷,類似這樣。

1# ...
2assert calculator.add(1, 1) == 2
3# ...

RSpec 加入了匹配器的設計,讓我們可以用 is expected to equal 2 這樣接近人類可以理解的句子去撰寫測試,進而讓我們的測試案例更加容易閱讀。

Be

Be 是一個特殊類型的匹配器,我們可以用任何 be_ 作為開頭來進行布林類型的匹配。大多數人用過像是 be_truthybe_falsybe_nil 這三個比較常見的 Be 匹配器,然而 be_ 是適用所有布林方法。

我們以 Rack::Response 為例子,假設我們在處理了一個請求後希望得到成功的回傳,那們就可以用 Be 匹配器來處理。

1RSpec.describe SimpleController do
2  subject(:controller) { SimpleController.new }
3
4  describe '#index' do
5    subject { controller.index }
6
7    it { is_expected.to be_successful }
8  end
9end

我們之所以可以這樣使用,是因為 Rack::Response 物件包含了 #successful? 這個方法,因此 RSpec 會自動嘗試呼叫這個方法來進行比對。

也因此,我們在設計物件時,如果是回傳「布林值」類型的方法都應該使用 ? 結尾,這類似 Ruby 語言的一個慣例,也能讓其他人預期呼叫後回傳的型別。

另一個好處就是我們不用另外使用 describe 建立新的測試群組,就可以針對這類型的方法進行測試。

Change

Change(改變)匹配器也是我們在測試中很常使用的一個匹配器,我們可以用來確認呼叫「前後」的改變。

 1RSpec.describe Order do
 2  subject(:order) { Order.new }
 3
 4  describe '#add_item' do
 5    subject(:add_item) { order.add_item(OrderItem.new(amount: 100)) }
 6
 7    it { expect { add_item }.to change(order, :subtotal).from(0).to(100) }
 8    it { expect { add_item }.to change(order, :subtotal).to(100) }
 9    it { expect { add_item }.to change(order, :subtotal).by(100) }
10  end
11end

上面的範例展示了當我們呼叫 Order#add_item 前後的變化,根據使用情境我們可以使用 fromto 的組合,明確的指定從 0 變成 100 或者只使用 to 去表示「最終狀態」在一些情況下,我們可以用 by 來確認「變化量」像是 +1 或者 -1 這樣的情境。

Change 的使用也不限於數值,像是字串也可以用 fromto 來進行確認,唯一的限制是無法用 not_to 的條件使用,因此要檢查「減少」的情境可以用 by(-1) 而不是 not_to 搭配 by(1) 的方式。


如果想在第一時間收到更新,歡迎訂閱弦而時習之在這系列文章更新時收到通知,如果有希望了解的知識,可以利用優雅的 RSpec 測試回饋表單告訴我。