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

優雅的 RSpec 測試 - RSpec 概觀

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

幾乎所有的 Ruby 開發者都聽過 RSpec 然而 Rails 和 Ruby 都是使用名為 Minitest 的測試框架進行測試的,那麼 RSpec 做了什麼事情,讓更多開發者比起使用 Minitest 更喜歡使用 RSpec 呢?

DSL

DSL(Domain Specific Language,領域特定語言)是 Ruby 非常重要的語言特性,因為 Ruby 的類別(Class)本身也是物件,因此我們可以針對類別定義方法,同時允許省略括號等語法的特性,讓 DSL 在 Ruby 變得非常容易實現。

RSpec 就善用了這一點,讓我們能夠以 Ruby 撰寫出極度接近「人類語言」的測試,我們可以看一下這段範例。

 1RSpec.describe "User" do
 2  subject { User.new(admin: false) }
 3
 4  it { is_expected.not_to be_admin }
 5
 6  context 'when user is admin' do
 7    subject { User.new(admin: true) }
 8
 9    it { is_expected.to be_admin }
10  end
11end

閱讀起來基本上不太困難,可以看出來是描述「使用者」同時,一開始的對象是「非管理員使用者」並且,測試的案例為「預期不作為管理員」除此之外還有一個「當使用者是管理員」的情境,用「預期作為使用者」的測試案例。

假設我們要使用 Minitest 來撰寫這個測試,則會變成像這樣

 1require "minitest/autorun"
 2
 3class TestUser < Minitest::Test
 4  def test_user_not_be_admin
 5    @user = User.new(admin: false)
 6    assert_equal false, @user.admin?
 7  end
 8
 9  def test_user_be_a_admin
10    @user = User.new(admin: true)
11    assert_equal false, @user.admin?
12  end
13end

看起來似乎就沒那麼直覺,雖然有一部分原因是是因為對 Minitest 不太熟悉,然而這樣的撰寫方式仍讓人不是那麼容易學習。

現在 Minitest 有支援 Specs 的寫法,更接近 RSpec 的的寫法。

Behavior-Driven Development

除了 DSL 的特性外,RSpec 本身也是一個「行為驅動開發」的測試框架。我們在開發軟體的過程中,會很習慣的以「物件」或者「方法」的角度去思考,然而在 RSpec 中我們更關注的是對「行為」進行測試。

類似於上一個章節提到的 A-TDD(驗收測試區動開發)的方式,我們希望在實作之前先撰寫測試,如果 A-TDD 是針對「功能」來撰寫,那麼 BDD 則是針對「行為」來撰寫。

功能和行為的差異在哪裡呢?簡單來說「功能」是使用者一系列操作的組合,而行為則是某個程式邏輯,也就是說非常適合用來撰寫「單元測試」類型的測試案例。

我在 LeSS in Action 課程中學到的一個有趣觀念,單元測試跟端對端測試(E2E Testing)的差異,主要是「範圍」的大小,而 BDD 大多剛好是一個「單元」大小的測試。

舉例來說,我們想要實作一個「訂閱」的動作,就可以像這樣撰寫一個測試

 1RSpec.describe User do
 2  # ...
 3
 4  describe '#subscribe!' do
 5    subject(:subscribe) { user.subscribe! }
 6
 7    it { is_expected.to be_truthy }
 8
 9    context 'when use is subscribed' do
10      before { subscribe }
11
12      it { expect { subscribe }.to raise_error(User::AlreadySubscribed) }
13    end
14  end
15end

我們可以描述一個 #subscribe! 方法定為這個測試的主題,如果一切正常的話則回傳 true 除此之外,有一個「已經訂閱」的情境,我們先在測試前執行一次「訂閱」當我們再次驗證「訂閱」動作時,就會預期拋出 User::AlreadySubscribed 的例外,撰寫測試就變得相當輕鬆。

我認為這樣直覺的撰寫方式,以及完善的生態系,是讓 RSpec 深受大部分 Ruby 開發者喜愛的原因。


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