最近我讀了一篇在 Objective-C 中使用 control swizzling 測試 UIAlertController 的 文章 。這樣的文章總是促使我尋找一種不使用 control swizzling 也可以測試同樣東西的方法。雖然,我知道 swizzling 是開發人員的一個非常有力的工具,但我個人是儘可能去避免去使用它的。事實上,在最近的六年時間裡,我只在一個應用上用了 swizzling。所以我相信我們現在可以不使用 swizzling 來實現測試。
那麼問題來了,如何在 Swift 中不使用 swizzling 來對 UIAlertController 進行測試?
我們先從我們要測試的代碼開始吧。我已經添加一個按鈕到 Storyboard 中。(我之所以使用 Storyboard 為了讓那些不想用代碼寫介面的小夥伴有個更直觀的感受)當按下這個按鈕就會出現一個彈窗(alert),它有標題、訊息內容,還有兩個按鈕,分別是 OK 和取消(Cancel)。
下面是這段代碼:
import UIKitclass ViewController: UIViewController { var actionString: String? @IBAction func showAlert(sender: UIButton) { let alertViewController = UIAlertController(title: "Test Title", message: "Message", preferredStyle: .Alert) let okAction = UIAlertAction(title: "OK", style: .Default) { (action) -> Void in self.actionString = "OK" } let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) -> Void in self.actionString = "Cancel" } alertViewController.addAction(cancelAction) alertViewController.addAction(okAction) presentViewController(alertViewController, animated: true, completion: nil) }}
注意,在這個例子中彈窗動作沒有做什麼具體的操作,他們只表示能驗證單元測試。
讓我們開始一個簡單的測試:測試這個彈窗控制器的標題和訊息內容。
測試的代碼如下:
import XCTest@testable import TestingAlertExperimentclass TestingAlertExperimentTests: XCTestCase { var sut: ViewController! override func setUp() { super.setUp() sut = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as! ViewController UIApplication.sharedApplication().keyWindow?.rootViewController = sut } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() }}```
我們需要設定 sut 為根視圖控制器,否則視圖控制器不能彈出這個彈窗視圖控制器。
添加 UIAlertController 測試標題的代碼如下:
```Swiftfunc testAlert_HasTitle() { sut.showAlert(UIButton()) XCTAssertTrue(sut.presentedViewController is UIAlertController) XCTAssertEqual(sut.presentedViewController?.title, "Test Title")}```
這很簡單。現在讓我們測試 UIAlertController 的取消按鈕。這裡有一個問題:無法擷取彈窗動作的閉包。因此我們需要類比彈窗動作,為了儲存這個 handler 並在測試中調用它,看彈窗動作是否和我們預期的一樣。在測試案例中添加這樣一個類:
```Swiftclass MockAlertAction : UIAlertAction { typealias Handler = ((UIAlertAction) -> Void) var handler: Handler? var mockTitle: String? var mockStyle: UIAlertActionStyle convenience init(title: String?, style: UIAlertActionStyle, handler: ((UIAlertAction) -> Void)?) { self.init() mockTitle = title mockStyle = style self.handler = handler } override init() { mockStyle = .Default super.init() }}
這個類比類的主要工作是捕獲 handler 塊,以備後用。現在我們需要將這個類比的類插入到實現代碼中。將視圖控制器中的代碼換成下面這個:
import UIKitclass ViewController: UIViewController { var Action = UIAlertAction.self var actionString: String? @IBAction func showAlert(sender: UIButton) { let alertViewController = UIAlertController(title: "Test Title", message: "Message", preferredStyle: .Alert) let okAction = Action.init(title: "OK", style: .Default) { (action) -> Void in self.actionString = "OK" } let cancelAction = Action.init(title: "Cancel", style: .Cancel) { (action) -> Void in self.actionString = "Cancel" } alertViewController.addAction(cancelAction) alertViewController.addAction(okAction) presentViewController(alertViewController, animated: true, completion: nil) }}```
我們添加了一個類變數`Action`,並設定為`UIAlertAction.self`。這個變數我們會在初始化彈窗動作時使用。這就能讓我們在測試時可以重寫它。像這樣:
```Swiftfunc testAlert_FirstActionStoresCancel() { sut.Action = MockAlertAction.self sut.showAlert(UIButton()) let alertController = sut.presentedViewController as! UIAlertController let action = alertController.actions.first as! MockAlertAction action.handler!(action) XCTAssertEqual(sut.actionString, "Cancel")}
首先我們插入了這個彈窗動作。之後我們調用代碼彈出彈窗視圖控制器。我們從呈現的視圖控制器中擷取了取消動作,並且成功調用了捕獲的 handler 塊。最後一步就是去斷言當前的動作是否和我們預期的一樣。
就是這樣,一種很簡單的又不使用 swizzling 來測試 UIAlertViewController 的方式。
以上內容是關於在 Swift 中測試 UIAlertController的方法,希望對大家有用。