Reputation: 3221
I'm attempting to write a unit test for a class extension in Swift. The class extension itself will present a UIAlert
with a specified title and message as such:
extension UIViewController {
func presentAlert(title: String, message : String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
alertController.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.Default, handler: nil))
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
}
}
I created a file for my unit test containing the code below:
import XCTest
class AlertTest: XCTestCase {
func testAlert() {
let alert = presentAlert("Presented Alert", "This is an Alert!")
}
}
However, I keep getting an error of "Use of unresolved identifier 'presentAlert'"
. I tried adding public
to my extension after consulting this SO thread:
public func presentAlert(title: String, message : String)
but still no luck. Anyone have some insight?
EDIT
Based on the answer by @hkgumbs, this is my current code for my alert extension:
import Foundation
protocol Presentable {}
extension UIViewController {
public func presentAlert(title: String, message : String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
alertController.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.Default, handler: nil))
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
}
}
In the view controller, where I want to show the alert, this would still be the correct way to call my alert, correct?
self.presentAlert("Invalid URL", message: "Please try again")
Secondly, based on your comment, this is what I what I understand by calling Presentable
on a dummy value, but it's incorrect as SomethingPresentable
has no member PresentAlert
. Where I am going wrong in my understanding?
func testAlert() {
let app = XCUIApplication()
struct SomethingPresentable: Presentable {}
SomethingPresentable.presentAlert("Presented Alert", message: "This is an Alert!")
XCTAssert(app.alerts["Presented Alert"].exists)
app.alerts["Presented Alert"].tap();
}
EDIT 2 @hkgumbs, based on your newest comment, this is what I have for the extension:
import Foundation
protocol Presentable {}
extension Presentable {
func presentAlert(title: String, message : String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
alertController.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.Default, handler: nil))
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
}
}
And this is how I'm trying to call it from my ViewController:
Presentable.presentAlert("Invalid URL", message: "Please try again")
However, I get an error of "Use of instance member presentAlert
on type Self
; did you mean to use a variable of type Self
instead?"
Then, I'm guessing this is how the test would look?
func testAlert() {
let app = XCUIApplication()
struct SomethingPresentable: Presentable {}
SomethingPresentable.presentAlert("Presented Alert", message: "This is an Alert!")
XCTAssert(app.alerts["Presented Alert"].exists)
app.alerts["Presented Alert"].tap();
}
Upvotes: 12
Views: 9663
Reputation: 328
As @dan alluded to, you need to call it from an instance of the UIViewController
. Usually you don't want to instantiate framework objects in your tests if you can avoid it, so here are some options to avoid that:
presentAlert
static so that you can just UIViewController.presentAlert
presentAlert
a free function (don't put it in the extension)protocol Presentable {}
extension Presentable {
func presentAlert(title: String, message : String) { /* ... */ }
}
Then, whenever you need it you can extension UIViewController: Presentable {}
. And in your tests, you can just use a dummy class. The added benefit with this approach is that you can reuse that function on any type if needed, without globally exposing it when you don't.
Addendum
When we extend the protocol we are saying "anything that implements this protocol will get this method for free." The trick here is that this protocol is empty and, therefore, very easy to "implement."
extension YourViewController: Presentable {}
Upvotes: 7