在Swift中为类扩展编写单元测试

nar*_*ner 6 unit-testing ios xctest swift

我正在尝试为Swift中的类扩展编写单元测试.类扩展本身将呈现UIAlert具有指定标题和消息,如下所示:

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)

    }
}
Run Code Online (Sandbox Code Playgroud)

我为我的单元测试创​​建了一个包含以下代码的文件:

import XCTest

class AlertTest: XCTestCase {

    func testAlert() {

        let alert = presentAlert("Presented Alert", "This is an Alert!")

    }

}
Run Code Online (Sandbox Code Playgroud)

但是,我一直收到错误"Use of unresolved identifier 'presentAlert'".public在咨询了这个SO线程后,我尝试添加到我的扩展中:

public func presentAlert(title: String, message : String)

但仍然没有运气.有人有见识吗?

编辑

基于@hkgumbs的答案,这是我的警报扩展的当前代码:

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)

    }
}
Run Code Online (Sandbox Code Playgroud)

在视图控制器中,我想要显示警报,这仍然是调用警报的正确方法,对吗?

self.presentAlert("Invalid URL", message: "Please try again")
Run Code Online (Sandbox Code Playgroud)

其次,根据你的评论,这就是我通过调用Presentable虚拟值来理解我所知道的,但它是不正确的,因为SomethingPresentable没有成员PresentAlert.我理解的地方出错了?

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();

}
Run Code Online (Sandbox Code Playgroud)

编辑2 @hkgumbs,根据您的最新评论,这是我对扩展的内容:

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)

    }
}
Run Code Online (Sandbox Code Playgroud)

这就是我试图从我的ViewController调用它的方式:

Presentable.presentAlert("Invalid URL", message: "Please try again")
Run Code Online (Sandbox Code Playgroud)

但是,我收到一个错误" presentAlert在类型上使用实例成员Self;你的意思是使用类型的变量Self吗?"

然后,我猜这是测试的样子?

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();

}
Run Code Online (Sandbox Code Playgroud)

小智 6

正如@丹所提到的,你需要从调用它的实例UIViewController。如果可以避免的话,通常你不想在测试中实例化框架对象,所以这里有一些选项可以避免这种情况:

  1. 使presentAlert静态,这样你就可以UIViewController.presentAlert
  2. presentAlert一个免费的函数(不要放在扩展里)
  3. 改为扩展协议——我认为这是最干净的选择

protocol Presentable {}

extension Presentable {
    func presentAlert(title: String, message : String) { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)

然后,只要您需要,就可以extension UIViewController: Presentable {}。在你的测试中,你可以只使用一个虚拟类。这种方法的额外好处是,如果需要,您可以在任何类型上重用该函数,而无需在不需要时全局公开它。

附录

当我们扩展协议时,我们说“任何实现这个协议的东西都将免费获得这个方法。” 这里的技巧是这个协议是空的,因此很容易“实现”。

extension YourViewController: Presentable {}
Run Code Online (Sandbox Code Playgroud)