在Swift中为类扩展编写单元测试 [英] Writing a unit-test for a class extension in Swift

查看:109
本文介绍了在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:

  1. presentAlert设为静态,以便您只需UIViewController.presentAlert
  2. presentAlert提供免费功能(请勿将其放在扩展名中)
  3. 改为扩展协议–我认为这是最干净的选择
  1. Make presentAlert static so that you can just UIViewController.presentAlert
  2. Make presentAlert a free function (don't put it in the extension)
  3. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆