over 3 years ago

國外的 Rails 社群認為寫測試已經像是呼吸一樣再自然不過的事情,他們認為測試給工程師帶來許多好處,例如:可以即時驗證程式碼的結果、可以釐清程式設計的思考邏輯、增加網站的維護性等。

然而,DHH 認為任何測試都是需要成本的 (Testing like the TSA)

Every line of code you write has a cost. It takes time to write it, it takes time to update it, and it takes time to read and understand it. Thus it follows that the benefit derived must be greater than the cost to make it.

DHH

站在專案公司的角度來看,每個專案在時間與金錢有限之下,在時程內 deliver 網站並且上線已經是 First Priority 的目標,測試的程度和涵蓋範圍似乎從來不在考量之內,肉測 (肉眼測試、人體手動測試) 也一樣可以達到驗證的效果。 運氣好的人可以順利過關,運氣差一點的則陷入時程 delay 的泥沼裡。 為確保專案的維護性能夠維持在某個程度之上,我們應該集中火力針對某些情況寫測試。

單一行為串聯太多 association

我們以購物車下訂單的例子來看

# cart model

class Cart < ActiveRecord::Base
  has_many :cart_items
  has_many :cart_budnels
end

# order model

class Order < ActiveRecord::Base
 has_many :order_items
 has_many :order_budnels
end

當購物車的品項需要轉換成訂單的時候,此時必需根據cart 的 cart_items 資訊來新增 order 底下的 order_items
這行為可能的結果有:

  • 如果有 Cart Item,沒有Cart Bundle,就只新增 Order Item
  • 如果有 Cart Bundle,沒有Cart Item,就只新增 Order Bundle
  • 如果有 Cart Item、Cart Bundle,就兩個都新增
  • 如果沒有 Cart Item、Cart Bundle,就都不要新增

我們必需手動產生符合 四種結果 的資料來測試。

但萬一今天客戶需要加價購的功能呢?

# cart model

class Cart < ActiveRecord::Base
  has_many :cart_items
  has_many :cart_budnels
  has_many :cart_extra_purchases
end

# order model

class Order < ActiveRecord::Base
 has_many :order_items
 has_many :order_budnels
 has_many :order_extra_purchases
end

我們必需手動產生符合 更多不同的結果 的資料來測試。
假設後續客戶還需要修改相關的行為,又必需再用同樣耗時的手法測試。

單一行為需要判斷多重條件

我們以驗證 coupon 有無效果的例子來看

class Coupon < ActiveRecord::Base
  def self.valid?(code, amount)
    response = {:valid => false}
    
    coupon = find_by_code(code)
  
    if coupon
      if coupon.quantity > 0
        if (coupon.start_date.beginning_of_day...coupon.end_date.end_of_day).cover?(DateTime.now)
          if amount > coupon.price_minimum
            response = {:valid => true, :msg => "Coupon code valid"}
          else
            response = {:msg => "This coupon code has minimum price of NTD #{coupon.price_minimum}"}
          end
        else
          response = {:msg => "This coupon code has been expired."}
        end
      else
        response = {:msg => "This coupon code is not available."}
      end
    else
      response = {:msg => "This coupon code is not valid."}
    end
    
    response
  end  
end

當使用者結帳時填入 coupon ,系統必需驗證這個 coupon 的有效性。
這行為可能的結果有:

  • 如果沒有 coupon
  • 如果 coupon 數量不夠
  • 如果 coupon 過期
  • 如果 coupon 低於消費金額門檻
  • 如果 coupon 有效

我們同樣必需耗費許多時間手動產生符合 五種結果 的資料來測試。

API 的串接

系統需要串接第三方服務的時候

class AlipayController < ApplicationController
  def complete
    if params[:alipay].present?
      if Order.find([:alipay][:order_id])
        process_order_paying
        redirect_to checkout_complete_path, :notice => "支付寶 付款成功"
      else
        redirect_to checkout_failed_path, :notice => "支付失敗, 請與本站聯絡."
      end
    else
      redirect_to root_path, :notice => result[:msg]
    end
  end
end

系統得根據外部回傳的 response 進行結帳後的動作。
我們得把時間花在不斷地執行結帳流程來獲得 response 以便修改程式碼。

註:DHH 的 Testing like the TSA 另外談論到 7 點關於 test 的想法,有興趣的可以去看一下。

← What Is Proc 如何開始一個簡單的 RSpec 測試? →
 
comments powered by Disqus