為什麼要寫測試

  • 為了追求品質更好的代碼
  • 為了避免發生過的錯誤再次發生
  • 為了增加重構的信心
  • 為了尋找更好的實作方式

測試好處(或是壞處?)Q & A

測試幫助檢查代碼正確性

客戶花錢就是讓我們寫出符合客戶需求(正確)的代碼,寫測試來檢查代碼的正確性。通常大家可能寫好代碼,打開瀏覽器,刷新,改代碼,刷新,重複一整天,日復一日。一個功能可能很複雜,需要考慮很多情況,手工測試會疏漏。而項目變得龐大複雜的時候,手測不僅麻煩,又容易漏掉。

測試節省現在的時間

一天手測一兩百次 VS 自動測試,那個省時間呢?省下來的時間可以拿來做別的事情。譬如 200 個小時就可以開發出一個 Basecamp,Life is just too short to test manually。

買未來的時間

項目進行了 6 個月後,怎麼樣確保上了新功能,不會弄壞舊功能(這有一個術語叫做 Regression,本來的功能改了以後壞掉了的意思)。可以靠測試,幫我們找出現有的代碼,是否還像當初一樣的運作。測試可以檢查功能的正確性,確保功能現在可以用,未來也可以用!項目複雜以後要修一個 Bug,或是加新功能都越來越困難,有測試可以讓你檢查現有的功能有沒有壞,上新功能可以發現弄壞了什麼舊功能,節省下未來的時間。

升級好幫手

Rails 出 5.0 了,升級從來都不是一件開心的事情,但是還是要升級啊,怎麼辦呢!沒有測試的 Rails 項目跟有測試的 Rails 項目,那個容易升級,升級之後能確保現有功能仍正常作動呢?答案顯而易見。

信心十足

出去聚會跟人攀談時,總是會聊到,你們有寫測試嗎?用什麼寫測試?這時候有寫測試不但讓你講話的聲音可以大幾分貝,充滿信心。有測試在交付網站時,交付功能時,有那個信心確保功能是會動的,功能是正確的。充滿信心做起任何事情來都更快更好。倘若是按代碼行數收費的開發者,寫測試還能多收點錢哦。

重構更簡單

重構就是對一段代碼進行修改,但保留原有的功能性,使代碼更短小精悍。重構很重要,但怎麼知道重構有沒有弄壞東西?測試,有測試重構起來更簡單。

寫測試,開發速度變慢

本來寫一份代碼,現在寫兩份代碼,一份是代碼,另一份是測試,寫兩份當然慢!但只是一開始慢,長期下來快多了,慢在當下,贏在未來。當項目越來越龐大複雜,具有良好完善測試的項目跟毫無測試的項目,那一個加新功能更容易?更能確保改東不會改壞西?

速度重要,品質也很重要

寫測試要寫兩份代碼,比不寫測試當然來得慢!但不只是求快而已,還要講究品質。擁有完善測試的代碼品質,經過英國皇家學院研究證明,有測試的代碼品質,97 % 要比沒有測試的代碼來得高。很難測的代碼通常就是實作不好,實作好的代碼通常很容易測試。

一錯再錯

寫代碼難免會出錯,出錯非常好!起碼發現了問題在那裡,那怎麼保證之後不再出錯,寫一個重現錯誤的測試,這樣可以確保之後新加的代碼,沒有弄壞以前已經出錯過的問題。

客戶不在乎啊,幹嘛寫測試?

這取決於你要賣給客戶的是什麼,是軟體開發的工匠精神,還是得過且過?

開發者的責任

寫測試是開發者的責任,為了自己,也為了未來接手自己項目的人好,大家都好,一起寫測試!

測試不是 QA 在做的?

一套大型的商業軟體,可能有專職的 QA 在做測試。但 Rails 的開發者通常是前後端都自己來,測試自己寫也是很合理的。Rails 通常是 Web 創業者所選擇的框架,這些創業者有錢嗎?沒有阿!此外,誰會比功能的實作者更了解需求與行為呢?所以開發者自己寫測試。

最好的文件就是測試

撰寫的再好的文件,還是看不懂,因為開發者通常不看文件...。一個冗長的文件跟本身就是代碼的測試那個比較好?測試裡面有每個功能怎麼使用的詳細示範。代碼留下來的文件通常就是註解,或是用註解產生出來的 API 文件,這些文件隨著時間推移,日換星移,人來人去,很早就不合時宜了,所以測試還是比文件好,通過的測試就是好文件。

功能實作的引導

實作功能時,首先寫個不通過的測試,測試會告訴你下一步該做什麼,接著做,直到測試跑通為止。Test-driven Development,又稱 TDD。

TDD

  1. 先寫測試
  2. 想最簡單的辦法讓測試通過
  3. 把已經可以用的代碼重構

TDD 推薦書:Test Driven Development: By Example | Kent Back

我聽人說 TDD 已死

沒錯,TDD 已經死了,但你沒有聽到後面那一句嗎?DHH 在 2014 年的 RailsConf 上提示:「TDD is dead. Long Live testing」後面那句就是測試永生!他的意思是說,什麼事情都要先寫測試才可以的狂熱份子,他以前認同,但現在表示不認同,但交付的代碼最終是需要有測試的。

有人說什麼紅綠重構,那是什麼?

測試失敗時通常會用紅色顯示,測試通過則為綠色,藉由撰寫失敗的測試,修改代碼使其通過,測試通過之後,開始整理代碼,去除代碼重複的部份(Don't Repeat Yourself),也就是重構。重構又會使測試失敗,再努力使其通過,如此不斷反覆,就可以實作功能,並產出高品質的代碼。

步驟大致為:

1. 寫測試
2. 跑測試
  測試通過 => 3-A 開始重構
  測試失敗 => 3-B 撰寫代碼使其通過
3-A. 重構,前往 4.
3-B. 撰寫代碼,回到 2. 跑測試
4. 跑測試
5-A. 測試失敗 => 回到 3-A 繼續完成重構
5-A. 測試通過 => 結束

測試的分類

簡單分成「單元測試」、「整合測試」以及「驗收測試」。

單元測試(Unit Test)

測試一個 Class、Module 裡面的方法。

譬如:測試一個 Active Record Model 的 Scope、Class 的公有方法。

整合測試(Intergration Test)

測試 Class/Module 之間的互動。

譬如:測試 Background Job 是不是有正確執行任務裡要做的事情。

驗收測試(Acceptance Test)

測試使用者的行為,譬如輸入帳號密碼,點擊登錄按鈕。這就像是從外到內的測試,從系統外面(使用者),一路測回系統內部,把驗收測試改到通過即可。已經知道使用者會如何使用這個功能的時候可以先進行驗收測試。

譬如:

visit login_page
fill_in username, with: "JuanitoFatas"
fill_in password, with: "1password"
click_on "Sign UP"

執行時就會告訴你,沒有 Controller,沒有 View,沒有 Model 等等,你一路從系統外面,透過驗收測試,幫助你實現這個功能需要的代碼。

測試的指標

速度

測試跑得快很重要,因為跑得慢你就不會想跑它了!跑得快可以更快得到測試的反饋,更快得到反饋,就可以更快得實作出功能。功能做得越快交付得也越快!執行時間慢到讓你會去看 Twitter、Facebook 就不夠快!

覆蓋率

所有公開可以用的方法(也就是有在用的功能)都要測。這樣不小心拿掉不該拿的東西的時候,才可以發現什麼東西壞掉了。但不是追求 100% 覆蓋(參考過度追求 Test 造成的設計傷害),目標是寫最少的測試,測出最多的東西。

各自獨立

好的測試應該是可以獨自測試,而不是相依別的測試,這樣在開發的時候才可以只跑自己那部分的測試(剩下的交給 CI (持續集成,後面的章節有介紹)去跑就好)。

容易維護

加新的測試很簡單。很難加新的測試,那你就再也不會寫測試了。

耦合性

測試之間彼此互相獨立,不會改東壞西。

可讀性佳

由於 RSpec 提供較親近人類的 DSL,讓我們的測試讀起來就像文件一樣。跑起來輸出時也有選項可以看起來像文件。還有可讀性沒有絕對的標準,你們團隊大家公認最佳的可讀性,那就是最好的!如果覺得那個部分不好讀,就說服大家吧!

results matching ""

    No results matching ""