國外的 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.
站在專案公司的角度來看,每個專案在時間與金錢有限之下,在時程內 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 的想法,有興趣的可以去看一下。