最近我讀了一篇在 Objective-C 中使用 control swizzling 測(cè)試 UIAlertController 的 文章 。這樣的文章總是促使我尋找一種不使用 control swizzling 也可以測(cè)試同樣?xùn)|西的方法。雖然,我知道 swizzling 是開發(fā)者的一個(gè)非常有力的工具,但我個(gè)人是盡可能去避免去使用它的。事實(shí)上,在最近的六年時(shí)間里,我只在一個(gè)應(yīng)用上用了 swizzling。所以我相信我們現(xiàn)在可以不使用 swizzling 來(lái)實(shí)現(xiàn)測(cè)試。
那么問題來(lái)了,如何在 Swift 中不使用 swizzling 來(lái)對(duì) UIAlertController 進(jìn)行測(cè)試?
我們先從我們要測(cè)試的代碼開始吧。我已經(jīng)添加一個(gè)按鈕到 Storyboard 中。(我之所以使用 Storyboard 為了讓那些不想用代碼寫界面的小伙伴有個(gè)更直觀的感受)當(dāng)按下這個(gè)按鈕就會(huì)出現(xiàn)一個(gè)彈窗(alert),它有標(biāo)題、消息內(nèi)容,還有兩個(gè)按鈕,分別是 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) }} 注意,在這個(gè)例子中彈窗動(dòng)作沒有做什么具體的操作,他們只表示能驗(yàn)證單元測(cè)試。
讓我們開始一個(gè)簡(jiǎn)單的測(cè)試:測(cè)試這個(gè)彈窗控制器的標(biāo)題和消息內(nèi)容。
測(cè)試的代碼如下:
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() }}``` 我們需要設(shè)置 sut 為根視圖控制器,否則視圖控制器不能彈出這個(gè)彈窗視圖控制器。
添加 UIAlertController 測(cè)試標(biāo)題的代碼如下:
```Swiftfunc testAlert_HasTitle() { sut.showAlert(UIButton()) XCTAssertTrue(sut.presentedViewController is UIAlertController) XCTAssertEqual(sut.presentedViewController?.title, "Test Title")}``` 這很簡(jiǎn)單。現(xiàn)在讓我們測(cè)試 UIAlertController 的取消按鈕。這里有一個(gè)問題:無(wú)法獲取彈窗動(dòng)作的閉包。因此我們需要模擬彈窗動(dòng)作,為了存儲(chǔ)這個(gè) handler 并在測(cè)試中調(diào)用它,看彈窗動(dòng)作是否和我們預(yù)期的一樣。在測(cè)試用例中添加這樣一個(gè)類:
```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() }} 這個(gè)模擬類的主要工作是捕獲 handler 塊,以備后用?,F(xiàn)在我們需要將這個(gè)模擬的類插入到實(shí)現(xiàn)代碼中。將視圖控制器中的代碼換成下面這個(gè):
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) }}``` 我們添加了一個(gè)類變量`Action`,并設(shè)置為`UIAlertAction.self`。這個(gè)變量我們會(huì)在初始化彈窗動(dòng)作時(shí)使用。這就能讓我們?cè)跍y(cè)試時(shí)可以重寫它。像這樣:
```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")} 首先我們插入了這個(gè)彈窗動(dòng)作。之后我們調(diào)用代碼彈出彈窗視圖控制器。我們從呈現(xiàn)的視圖控制器中獲取了取消動(dòng)作,并且成功調(diào)用了捕獲的 handler 塊。最后一步就是去斷言當(dāng)前的動(dòng)作是否和我們預(yù)期的一樣。
就是這樣,一種很簡(jiǎn)單的又不使用 swizzling 來(lái)測(cè)試 UIAlertViewController 的方式。
以上內(nèi)容是關(guān)于在 Swift 中測(cè)試 UIAlertController的方法,希望對(duì)大家有用。



















