在 Swift 中为 class 扩展编写 unit-test
Writing a unit-test for a class extension in Swift
我正在尝试为 Swift 中的 class 扩展编写单元测试。 class 扩展本身将显示一个 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")
但是,我收到 "Use of instance member presentAlert
on type Self
; did you mean to use a variable of type Self
instead?"
的错误
那么,我猜这就是测试的样子?
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();
}
正如 @dan 提到的,您需要从 UIViewController
的 实例 调用它。如果可以避免,通常您不想在测试中实例化框架对象,因此这里有一些选项可以避免这种情况:
- 使
presentAlert
静态,这样你就可以 UIViewController.presentAlert
- 使
presentAlert
成为一个自由函数(不要放在扩展中)
- 改为扩展协议——我认为这是最干净的选择
protocol Presentable {}
extension Presentable {
func presentAlert(title: String, message : String) { /* ... */ }
}
然后,只要你需要,你就可以extension UIViewController: Presentable {}
。在你的测试中,你可以只使用一个虚拟 class。这种方法的额外好处是,如果需要,您可以在任何类型上重用该函数,而无需在不需要时全局公开它。
附录
当我们扩展我们说的协议时 "anything that implements this protocol will get this method for free." 这里的技巧是这个协议是空的,因此很容易 "implement."
extension YourViewController: Presentable {}
我正在尝试为 Swift 中的 class 扩展编写单元测试。 class 扩展本身将显示一个 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")
但是,我收到 "Use of instance member presentAlert
on type Self
; did you mean to use a variable of type Self
instead?"
那么,我猜这就是测试的样子?
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();
}
正如 @dan 提到的,您需要从 UIViewController
的 实例 调用它。如果可以避免,通常您不想在测试中实例化框架对象,因此这里有一些选项可以避免这种情况:
- 使
presentAlert
静态,这样你就可以UIViewController.presentAlert
- 使
presentAlert
成为一个自由函数(不要放在扩展中) - 改为扩展协议——我认为这是最干净的选择
protocol Presentable {}
extension Presentable {
func presentAlert(title: String, message : String) { /* ... */ }
}
然后,只要你需要,你就可以extension UIViewController: Presentable {}
。在你的测试中,你可以只使用一个虚拟 class。这种方法的额外好处是,如果需要,您可以在任何类型上重用该函数,而无需在不需要时全局公开它。
附录
当我们扩展我们说的协议时 "anything that implements this protocol will get this method for free." 这里的技巧是这个协议是空的,因此很容易 "implement."
extension YourViewController: Presentable {}