測試範圍

測試範圍

Posted by willsbor Kang on 2017-04-19

而哪些程式碼是需要被測試的呢?

理想上,只要是自己寫的就需要測試

像是 System function、3rd party library 應該就不需要為他們寫測試碼。

也就是說,我們會寫了一堆程式碼,來整合 System function、3rd party library 來實踐服務

所以我們會需要驗證服務的邏輯是否正確,而使用到的「工具」的反應是否正確,就不是我們要關心的。

因此,功能描述就顯得相對重要,他會決定了服務的面相和測試的範圍。

例如:

  • 我使用了系統提供的內建購買功能,來實踐使用者訂閱離線資料庫、自動更新等等功能

這樣的描述,很明確的綁定了一個系統服務,在實作上,可以很單一的考慮這項系統服務的特性來寫作商業邏輯,閱讀上也會很直覺,但是也很容易造成綁定太深,而無法測試。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyManager: SystemBuyDelegate {

var completionHandler: ((error) -> Void)?

func buy(_ completionHandler: ((error) -> Void)?) {
self.completionHandler = completionHandler
SystemBuy.default.delegate = self
SystemBuy.default.buy()
}

//:- SystemBuyDelegate

func buySuccess() {
completionHandler?(nil)
}

func buyFailed(error: Error) {
completionHandler?(error)
}
}

iOS 很多系統服務的實踐大概會向上面那樣,使用上很直覺,但是當我們要測試 buy() 時,就會發現一些問題

情境一: SystemBuy.default 只有在實機上有功能,模擬器不提供

當在這樣的狀況時,在模擬器上面跑的 Unit Test 就可能會 Failed

情境二: SystemBuy.default 執行過一次後,就會設定成特定狀態,要 reset 才會回來

跑 Unit Test 不可能隨時隨地 reset 模擬器,當測試跑 N 次, N 次結果都不一樣,會讓這樣的測試無法被信任

總歸一句話,當模組間相互依賴太深,就會難以測試;buy()這項功能綁訂SystemBuy.default太深,所以很難測試。

而上面的例子,我們究竟要測試什麼呢?我們要測試的範圍是我們寫過的流程,我們寫過的 code!

觀察一下上面這個模組可能會被如何應用,例如:

1
2
3
4
5
6
7
8
9
class BuyViewController: UIViewController {
...

func clickBuyButton() {
MyManager.sharedInstance.buy { (error) -> Void in
...
}
}
}

實作一個買東西的功能,介面通常會有一個買東西的按鈕,如果我們要說這個功能正常,也是說

當按下 BuyButton,進行了系統購買的動作,完成後會在 UI 上顯示相對應的動作

在上面的描述中,哪些事我們要測試的呢?

  • 按下 BuyButton
  • 進行系統購買動作
  • 完成後會顯示對應動作

在這裡我們並不會考慮 系統購買是否真的成功,因為系統購買根本不是我們寫的,我們只會在意成功時要做什麼動作,失敗時要做什麼動作。

MyManager 而言,他要對 BuyViewController 保證,當呼叫 buy() 之後,一定會有一個 completionHandler 被呼叫

只要 MyManager 能保證這件事情,也就能說 buy 的「UI 流程」是完整的。

所以當在設計 MyManager 時,要考慮到的就是,如何能疑除掉 SystemBuy.default,來測試所有我們自行設計的程式碼!

Finish Date: 2017-04-28