Recently I read an article about testing UIAlertController using control swizzling in Objective-C. Articles like this always prompted me to find a way to test the same thing without using control swizzling. Although, I know that swizzling is a very powerful tool for developers, I personally avoid using it as much as possible. In fact, in the last six years, I have used swizzling on only one app. So I believe we can now implement the test without using swizzling.
So the question is, how to test UIAlertController without using swizzling in Swift?
Let's start with the code we want to test. I've added a button to the Storyboard. (The reason I use Storyboard is to give those friends who don’t want to write interfaces with code to have a more intuitive feeling) When pressing this button, a pop-up window will appear, which has the title, message content, and two buttons, namely OK and Cancel.
Here is this code:
import UIKit class 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 = "OK" } let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) -> Void in = "Cancel" } (cancelAction) (okAction) presentViewController(alertViewController, animated: true, completion: nil) } }
Note that in this example, the pop-up action does not do any specific operations, they only indicate that they can verify unit tests.
Let's start a simple test: test the title and message content of this pop-up controller.
The test code is as follows:
import XCTest @testable import TestingAlertExperiment class TestingAlertExperimentTests: XCTestCase { var sut: ViewController! override func setUp() { () sut = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as! ViewController ().keyWindow?.rootViewController = sut } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. () } } ```
We need to set sut as the root view controller, otherwise the view controller cannot pop up this pop-up view controller.
The code to add the UIAlertController test title is as follows:
```Swift func testAlert_HasTitle() { (UIButton()) XCTAssertTrue( is UIAlertController) XCTAssertEqual(?.title, "Test Title") } ```
This is very simple. Now let's test the Cancel button of UIAlertController. Here is a problem: the closure of the pop-up action cannot be obtained. Therefore, we need to simulate pop-up action. In order to store this handler and call it in the test, see if the pop-up action is the same as we expected. Add a class like this in the test case:
```Swift class MockAlertAction : UIAlertAction { typealias Handler = ((UIAlertAction) -> Void) var handler: Handler? var mockTitle: String? var mockStyle: UIAlertActionStyle convenience init(title: String?, style: UIAlertActionStyle, handler: ((UIAlertAction) -> Void)?) { () mockTitle = title mockStyle = style = handler } override init() { mockStyle = .Default () } }
The main job of this mock class is to capture handler blocks for later use. Now we need to insert this mocked class into the implementation code. Change the code in the view controller to the following:
import UIKit class ViewController: UIViewController { var Action = var actionString: String? @IBAction func showAlert(sender: UIButton) { let alertViewController = UIAlertController(title: "Test Title", message: "Message", preferredStyle: .Alert) let okAction = (title: "OK", style: .Default) { (action) -> Void in = "OK" } let cancelAction = (title: "Cancel", style: .Cancel) { (action) -> Void in = "Cancel" } (cancelAction) (okAction) presentViewController(alertViewController, animated: true, completion: nil) } } ```
We added a class variable `Action` and set it to ``. We will use this variable when initializing the pop-up action. This allows us to rewrite it while testing. Like this:
```Swift func testAlert_FirstActionStoresCancel() { = (UIButton()) let alertController = as! UIAlertController let action = as! MockAlertAction !(action) XCTAssertEqual(, "Cancel") }
First we inserted this pop-up action. After that we call the code to pop up the view controller. We took the cancel action from the rendered view controller and successfully called the captured handler block. The last step is to assert whether the current action is the same as we expected.
That's it, a very simple way to test the UIAlertViewController without using swizzling.
The above content is about testing UIAlertController in Swift, I hope it will be useful to everyone.