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

Posted

技术标签:

【中文标题】Swift:如何在测试期间不加载 AppDelegate【英文标题】:Swift: How to not load AppDelegate during Tests 【发布时间】:2016-08-24 06:52:58 【问题描述】:

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

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

我尝试过的如下:

AppDelegate.swift 中删除了@NSApplicationMain,然后添加了一个main.swift,内容如下:

class FakeAppDelegate: NSObject, NSApplicationDelegate 


NSApplication.sharedApplication()
NSApp.delegate = FakeAppDelegate()
NSApplicationMain(Process.argc, Process.unsafeArgv)

这段代码在测试之前运行,但完全没有效果。

我可能不得不说:我的 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

【问题讨论】:

【参考方案1】:

只是对之前接受答案的更新,这是我的main.swift

private func isTestRun() -> Bool 
    return NSClassFromString("XCTestCase") != nil


if isTestRun() 
    // This skips setting up the app delegate
    NSApplication.shared.run()
 else 
    // For some magical reason, the AppDelegate is setup when
    // initialized this way
    _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

更紧凑一点!我正在使用 Swift 4.1 和 XCode 9.4.1

【讨论】:

【参考方案2】:

经过几天的尝试和失败,我在 Apple forums 上找到了答案:

问题是我的 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())

所以之前的实际问题是在测试期间加载了 Nib。该解决方案可以防止这种情况。它只是在检测到测试运行时使用模拟的应用程序委托加载应用程序(通过查找XCTest 类)。

我确定我将不得不对此进行更多调整。尤其是从 UI 测试开始时。但目前它有效。

【讨论】:

我收到Cannot convert value of type 'AppDelegate.Type' to expected argument type 'NSApplication'

以上是关于Swift:如何在测试期间不加载 AppDelegate的主要内容,如果未能解决你的问题,请参考以下文章

如何在小部件测试期间存根不属于类的函数?

Swift,NSCoding 保存类的数组不起作用

iOS 应用在导航期间迁移到 swift3 后崩溃

如何在 cypress 组件测试期间包装 vue 组件?

在 TestFlight 上测试 React Native App

无法在测试中将 App Delegate 强制转换为 App Delegate