【问题标题】:Writing a unit-test for a class extension in Swift在 Swift 中为类扩展编写单元测试
【发布时间】:2016-12-19 21:24:18
【问题描述】:

我正在尝试为 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)

    }
}

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

import XCTest

class AlertTest: XCTestCase {

    func testAlert() {

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

    }

}

但是,我不断收到"Use of unresolved identifier 'presentAlert'" 的错误消息。在咨询此SO thread 后,我尝试将public 添加到我的扩展中:

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)

    }
}

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

self.presentAlert("Invalid URL", message: "Please try again")

其次,根据您的评论,这就是我通过在虚拟值上调用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();

}

编辑 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)

    }
}

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

Presentable.presentAlert("Invalid URL", message: "Please try again")

但是,我收到错误消息“在类型 Self 上使用实例成员 presentAlert;您的意思是改用 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();

}

【问题讨论】:

  • 您的分机在UIViewController,因此您需要在UIViewController 上调用它。您调用它的方式也与声明不匹配。
  • @dan 感谢您发现这个错字...我刚刚在问题中修正了它。在单元测试中在 ViewController 上调用它会是什么样子?

标签: ios swift unit-testing xctest


【解决方案1】:

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

  1. presentAlert设为静态,这样您就可以只使用UIViewController.presentAlert
  2. presentAlert设为免费功能(不要放在扩展中)
  3. 改为扩展协议 - 我认为这是最简洁的选择

protocol Presentable {}

extension Presentable {
    func presentAlert(title: String, message : String) { /* ... */ }
}

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

附录

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

extension YourViewController: Presentable {}

【讨论】:

  • 这对我来说开始有些意义了,谢谢。给你两个问题:1)如果我将扩展改为协议,我将如何从我的视图控制器中调用它?目前,调用扩展看起来像这样: self.presentAlert("Invalid URL", message: "Please try again") 2) 我不完全确定使用虚拟类从我的测试中调用此协议是什么意思。 ..你能详细说明一下吗?谢谢 --- 仍在努力适应 Swift 的一些复杂之处!
  • 很高兴接受这个答案;只是需要更多的理解...谢谢!
  • @narner 当然! 1) 如果您扩展此协议,那么您将可以以相同的方式访问它:extension YourViewController: Presentable {}。 2) 在测试中创建一个虚拟值的想法相同:struct SomethingPresentable: Presentable {}。然后调用presentAlert 而不是ViewController
  • 我想我几乎在那里....我对我的原始帖子进行了编辑。你介意最后看一下吗?感谢您的帮助和耐心!!!
  • @narner 几乎是真的!所以你想要extension Presentable 而不是extension UIViewController。这将修复你的测试。然后为了获得 ViewController 上的功能,您需要我之前评论中的那一行。
猜你喜欢
  • 1970-01-01
  • 2017-05-29
  • 1970-01-01
  • 1970-01-01
  • 2012-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多