over 8 years ago
有同學最近在問 RSpec 的問題,因為會回答很多次,乾脆寫「一個經典例子」,來介紹 RSpec 的常見 syntax,來介紹一串重要的手法。
例子:Course#publish?
publish?
的判斷方式是 published_at
這個欄位存在與否。
class Course < ActiveRecord::Base
def published?
published_at.present?
end
end
觀念 1: 測試要寫 正 / 負場景
在這個例子裡面,因為是測 boolean 值,比較好的方式,是要寫正反例子。
RSpec.describe Course, type: :model do
describe "#publish?" do
it "return true when published_at present?" do
course = FactoryGirl.create(:course, published_at: Time.now)
expect(course.published?).to be_truthy
end
it "return false when published_at blank?" do
course = FactoryGirl.create(:course)
expect(course.published?).to be_falsey
end
end
end
觀念 2 :當 when 與重複語句出現,應抽出變成 context
- when published_at present?
- when published_at blank?
這兩句是場景,所以接下來可以改寫成
RSpec.describe Course, type: :model do
describe "#publish?" do
context "when published_at present?" do
it "return true" do
course = FactoryGirl.create(:course, published_at: Time.now)
expect(course.published?).to be_truthy
end
end
context "when published_at blank?" do
it "return false" do
course = FactoryGirl.create(:course)
expect(course.published?).to be_falsey
end
end
end
end
觀念 3 : 用 let 抽出 course,it 內只測 expect
將物件抽到 let。
RSpec.describe Course, type: :model do
describe "#publish?" do
context "when published_at present?" do
let(:course) { FactoryGirl.create(:course, published_at: Time.now) }
it "return true" do
expect(course.published?).to be_truthy
end
end
context "when published_at blank?" do
let(:course) { FactoryGirl.create(:course) }
it "return false" do
expect(course.published?).to be_falsey
end
end
end
end
觀念 4 : Minimal Valid Object 與「測試真正的變因」
但是在步驟 3 中,這樣的測試手法,是生成了「兩個不同的物件」去測。但我們寫這個測試的目的是要「測試一個物件,在published_at值不同」的情況下的回傳狀況。
真正的「宿主」是 Coruse 本身。所以我們改用 subject。傳 nil 與 現在的時間,去測試真正要測的行為。
RSpec.describe Course, type: :model do
describe "#publish?" do
subject(:course) { described_class.new :published_at => published_at }
context "when published_at present?" do
let(:published_at) { Time.now }
it "return true" do
expect(course.published?).to be_truthy
end
end
context "when published_at blank?" do
let(:published_at) { nil }
it "return false" do
expect(course.published?).to be_falsey
end
end
end
end
觀念 5 : xxxx? 可以使用 be_xxxxx 去測試
describe "#publish?" do
subject(:course) { described_class.new :published_at => published_at }
context "when published_at present?" do
let(:published_at) { Time.now }
it "return true" do
expect(course).to be_published
end
end
context "when published_at blank?" do
let(:published_at) { nil }
it "return false" do
expect(course).not_to be_published
end
end
end
觀念 6 :is_expected
測 return true/ false 看起來像是超多餘的。而且我們是對 subject 測,所以又可以改成 is_expected
RSpec.describe Course, type: :model do
describe "#publish?" do
subject(:course) { described_class.new :published_at => published_at }
context "when published_at present?" do
let(:published_at) { Time.now }
it { is_expected.to be_published }
end
context "when published_at blank?" do
let(:published_at) { nil }
it { is_expected.not_to be_published }
end
end
end
最後看起來是不是變得很好維護呢?