Swift:如何在测试期间不加载 AppDelegate

Phi*_*hil 7 macos cocoa swift

我有一个 OS X 应用程序,它在启动时从服务器加载一些数据并将通知推送到NSUserNotificationCenter. 现在我遇到的问题是,在我的单元测试期间也会发生这种情况。我还没有发现任何方法可以防止这种情况发生。当然,我可以存根 HTTP 负载。但在某些情况下,我想测试加载,然后无论如何都会发送通知。

我想要做的是让测试运行不加载AppDelegate而是一个我只用于测试的假测试。我找到了几个关于如何使用 的示例 [1] UIApplicationMain,您可以在其中传递特定的 AppDelegate 类名。对NSApplicationMain[2] 来说,这是不可能的。

我试过的是以下内容:

@NSApplicationMain从 中删除AppDelegate.swift,然后添加了main.swift具有以下内容的 :

class FakeAppDelegate: NSObject, NSApplicationDelegate {
}

NSApplication.sharedApplication()
NSApp.delegate = FakeAppDelegate()
NSApplicationMain(Process.argc, Process.unsafeArgv)
Run Code Online (Sandbox Code Playgroud)

此代码在测试之前运行,但根本没有任何效果。

我可能不得不说:我的 AppDelegate 几乎是空的。为了处理 MainMenu.xib 的东西,我制作了一个单独的视图控制器,它在awakeFromNib.

[1] http://www.mokacoding.com/blog/prevent-unit-tests-from-loading-app-delegate-in-swift/

[2] https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Miscellaneous/AppKit_Functions/#//apple_ref/c/func/NSApplicationMain

Phi*_*hil 3

经过几天的尝试和失败,我在苹果论坛上找到了答案:

问题是我的 main.swift 文件在初始化 NSApplication 之前初始化了我的 AppDelegate。Apple 文档明确指出,许多其他 Cocoa 类在初始化时都依赖 NSApplication 来启动和运行。显然,NSObject 和 NSWindow 是其中两个。

所以我的最终和工作代码main.swift如下所示:

private func isTestRun() -> Bool {
    return NSClassFromString("XCTest") != nil
}

private func runApplication(
    application: NSApplication = NSApplication.sharedApplication(),
    delegate: NSObject.Type?   = nil,
    bundle: NSBundle?          = nil,
    nibName: String            = "MainMenu") {

    var topLevelObjects: NSArray?

    // Actual initialization of the delegate is deferred until here:
    application.delegate = delegate?.init() as? NSApplicationDelegate

    guard bundle != nil else {
        application.run()
        return
    }

    if bundle!.loadNibNamed(nibName, owner: application, topLevelObjects: &topLevelObjects ) {
        application.run()
    } else {
        print("An error was encountered while starting the application.")
    }
}

if isTestRun() {
    let mockDelegateClass = NSClassFromString("MockAppDelegate") as? NSObject.Type
    runApplication(delegate: mockDelegateClass)
} else {
    runApplication(delegate: AppDelegate.self, bundle: NSBundle.mainBundle())
}
Run Code Online (Sandbox Code Playgroud)

所以之前的实际问题是笔尖在测试过程中被加载。该解决方案可以防止这种情况发生。每当检测到测试运行(通过查找类XCTest)时,它都会使用模拟的应用程序委托加载应用程序。

我确信我必须对此进行更多调整。特别是当开始进行 UI 测试时。但目前它有效。