有同學最近在問 RSpec 的問題,因為會回答很多次,乾脆寫「一個經典例子」,來介紹 RSpec 的常見 syntax,來介紹一串重要的手法。
publish?
的判斷方式是 published_at
這個欄位存在與否。
class Course < ActiveRecord::Base def published? published_at.present? end end
在這個例子裡面,因為是測 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
這兩句是場景,所以接下來可以改寫成
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
將物件抽到 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
但是在步驟 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
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
測 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
最後看起來是不是變得很好維護呢?