over 1 year 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

最後看起來是不是變得很好維護呢?

← Content Marketing 這樣做 - See / Think / Do framework [RSpec] 進階測試系列概念 - Part 0 →
 
comments powered by Disqus