# 撰寫測試的觀念

## 單元測試

### 單元測試的基本原則

1. 自動化，而且可以被重複執行
2. 應該很容易被實現
3. 它到第二天還有意義(代表不是臨時性的測試案例)
4. 任何人按個鈕就可以執行它
5. 它的執行速度應該很快
6. 它的執行結果應該一致
7. 它應該要能完全掌控被測試的單元
8. 它應該是完全被隔離的(測試案例之間彼此獨立)
9. 如果它的執行結果為紅燈，應該要能很簡單清楚的呈現我們的期望為何，發生問題的原因在哪

### 檢查是否為單元測試

1. 我兩週前所寫的單元測試，今年還能正常執行嗎?
2. 我兩個月前寫的單元測試，團隊內的任何一個人是否都可以正常執行並得到結果呢?
3. 我能在幾分鐘之內跑完全部的單元測試嗎?
4. 我能一件執行所有我寫過的單元測試嗎?
5. 我能在幾分鐘之內寫出一個基本的單元測試嗎?

撰寫單元測試的部分心得分享

1. 並非只有產品程式需要重構，測試程式也需要。盡量模組化，提升可讀性。

優秀的單元測試應該具備

1. 一段自動化的程式，它會呼叫另一個方法，然後驗證這方法或是該類別的邏輯行為的某些預期結果
2. 用一個自動化測試框架進行編寫
3. 容易撰寫
4. 執行快速
5. 能由開發團隊裡任何人重複執行且得到一樣的結果

## 名詞定義

1.  **回歸(regression)**: **當軟體一有變更時，我們必須重新測試現有功能，以便確認修改、新加入的部分沒有影響到既有功能，且達到預期的目的。**

    參考資料:

    科科和測試([https://kkboxsqa.wordpress.com/2014/02/27/%E5%9B%9E%E6%AD%B8%E6%B8%AC%E8%A9%A6-regression-testing/](https://kkboxsqa.wordpress.com/2014/02/27/%E5%9B%9E%E6%AD%B8%E6%B8%AC%E8%A9%A6-regression-testing/))

    wiki([https://zh.wikipedia.org/wiki/%E5%9B%9E%E5%BD%92%E6%B5%8B%E8%AF%95](https://zh.wikipedia.org/wiki/%E5%9B%9E%E5%BD%92%E6%B5%8B%E8%AF%95))
2. **遺留程式碼(legacy code)**:&#x20;
   1. 在維基百科中被定義為「一段作業系統或電腦科技都不再支援或製造的程式碼」，但是很多公司把任何比目前版本老舊的程式碼都稱為遺留程式碼。這個詞經常被用來形容那些難以維護使用、難以測試、通常也難以閱讀的程式碼。
   2. 只是能動的程式碼
   3. 沒有測試的程式碼
   4. **沒有測試程式保護的程式碼**
3. **重構:**\
   意味在**不改變功能的前提下**，修改程式碼，**改善其可讀性、可維護性**。例如你重新命名了一個方法，就算是進行了重構。把一個較大的方法分成多個較小的方法來使用，你也對程式進行了重構。**程式還是具備原本同樣的功能，但重構之後變得更容易維護、理解、偵錯和修改。**
4. **外部依賴(external dependency):**\
   是指在系統中的一個物件，它與被測試程式碼之間產生互動，但你無法掌控這個物件。(常見的外部依賴包括了檔案系統、執行緒、記憶體以及時間。)
5. **虛設常式(Stub):**\
   是在系統中產生一個可控的替代物件，來取代一個外部相依物件(或**協作者**)。你可以在測試程式中，透過虛設常式來避免必須直接相依物件所造成的問題。
6. **接縫**是指在程式碼中可以抽換不同功能的地方，這些功能例如: 使用虛設常式類別、增加一個建構函式









## 測試驅動開發

一旦瞭解了如何使用單元測試框架來撰寫結構化、可維護、可靠的測試，下一個問題就是什麼時候撰寫測試。很多人很直覺地認為替軟體撰寫測試的最佳時機是在完成軟體的產品程式之後，但越來越多人選擇在撰寫產品程式**之前**寫單元測試。這種方法稱之為**測試優先**或是**測試驅動開發(Test-Driven Development, TDD)**

TDD能協助建立高品質的產品程式碼與測試，也能協助驅動出更好的設計。但是也需要工程師付出更多(學習的時間、實作練習的時間，以及其他更多的知識)，當然這些付出是絕對值得的。

TDD 的技術相當簡單

1.  **撰寫一個會失敗的測試，以證明產品中程式或功能的缺失。**

    撰寫測試時，要假設產品程式碼已經可以運作，這樣測試失敗就可以證明程式碼有缺陷。
2.  **撰寫符合測試預期的產品程式碼，用以通過測試。**

    產品程式碼應該盡可能的簡單。
3.  **重構程式碼。**

    如果測試通過了，可以撰寫下一個單元測試，或是進行重構，以提升程式碼的可讀性，或是消除重複的程式碼。

如果一個測試失敗了，沒有經過修改它就再次成功了，你其實是在測試這個測試案例本身。如果你預期一個測試案例應該失敗，但它卻執行成功了，那這個測試本身可能有缺陷，或是測試的物件不對。

我們所討論的是**可讀、可維護、可靠的測試**，但是只有當你預期測試執行成功且真的成功，預期它失敗而且它真的失敗，才能確保這些測試的設計是正確無誤的。\
在這方面，TDD對你會有很大的幫助，這也是為什麼跟撰寫完產品程式後才撰寫測試單元測試相比，開發人員在使用TDD開發時，花在偵錯的時間會少很多的原因。\
如果開發人員信任它們的測試，他們不會只是為了以防萬一，而花時間去偵錯程式是否正確。\
這種信任只能透過測試的兩個方面來獲得：測試該失敗的時候失敗，該成功的時候成功。

### 核心技能

1. **僅僅做到先撰寫測試，並不能保證測試是可維護、可讀、可靠的。**
2. **僅僅做到撰寫出可維護、可讀、可靠的測試，並不能保證你能獲得測試先行的各種好處。**
3. **僅僅做到測試先行，並且可讀、可維護、可靠，並不能保證你能產出一個設計完善的系統。**\
   **設計能力才是使得程式碼優美，可維護的關鍵。**







