在Swift中为类扩展编写单元测试 [英] Writing a unit-test for a class extension in Swift
问题描述
我正在尝试为Swift中的类扩展编写单元测试.类扩展本身将显示带有指定标题和消息的UIAlert
,例如:
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!")
}
}
但是,我一直收到错误"Use of unresolved identifier 'presentAlert'"
.在咨询 SO线程:
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?
编辑
根据@hkgumbs的回答,这是我当前的警报扩展代码:
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")
第二,根据您的评论,这是我通过在虚拟值上调用Presentable
所了解的内容,但这是不正确的,因为SomethingPresentable
没有成员PresentAlert
.我的理解哪里出错了?
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();
}
编辑2 @hkgumbs,根据您的最新评论,这就是我的扩展名:
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)
}
}
这就是我试图从ViewController调用它的方式:
And this is how I'm trying to call it from my ViewController:
Presentable.presentAlert("Invalid URL", message: "Please try again")
但是,我收到错误消息在类型Self
上使用实例成员presentAlert
;您的意思是改为使用类型为Self
的变量?"
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();
}
推荐答案
如 @dan 所述,您需要从
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
设为静态,以便您只需UIViewController.presentAlert
- 为
presentAlert
提供免费功能(请勿将其放在扩展名中) - 改为扩展协议–我认为这是最干净的选择
- Make
presentAlert
static so that you can justUIViewController.presentAlert
- Make
presentAlert
a free function (don't put it in the extension) - Extend a protocol instead – I think this is the cleanest option
protocol Presentable {}
extension Presentable {
func presentAlert(title: String, message : String) { /* ... */ }
}
然后,只要需要,就可以extension UIViewController: Presentable {}
.在测试中,您可以只使用一个哑类.这种方法的额外好处是,您可以根据需要在任何类型上重用该函数,而不必在不需要时全局公开它.
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.
附录
扩展协议时,我们说的是:实现此协议的任何人都可以免费获得此方法."这里的窍门是该协议为空,因此很容易实现".
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 {}
这篇关于在Swift中为类扩展编写单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!