在不启动模拟器的情况下在Xcode 4中运行逻辑测试

31
我想在Xcode 4中使用OCUnit运行测试,而不启动模拟器。请不要试图说服我做单元测试的方式有误或类似的话语。我喜欢以传统方式进行TDD:在测试中编写类的API,然后使该类通过测试。我会编写单独的端到端测试,在模拟器中运行。
如果没有办法实现这一点,请告诉我如何让测试框架不实例化整个应用程序?我的应用程序是事件驱动的,启动时会发送大量事件,影响我的测试。
7个回答

28
请问有没有办法让测试工具不实例化整个应用程序?我的应用程序是事件驱动的,启动时会发送一系列事件,这些事件会影响我的测试。我正在使用 Xcode 4 内置的测试功能。虽然应用程序的实例化可能看起来很麻烦,但正如我在 Xcode 单元测试:好的、坏的和丑陋的 中所写的那样,它使得编写测试变得更加容易,无需区分逻辑测试和应用程序测试。特别是它让我可以为视图控制器编写单元测试。以下是我避免完整启动顺序的方法: 编辑方案 - 选择测试操作 - 在“测试”中选择“参数”选项卡 - 禁用“使用运行操作选项” - 添加一个环境变量,将 `runningTests` 设置为 `YES` 编辑您的应用代理 - 尽早在 `-application:didFinishLaunchingWithOptions:` 中添加以下内容:
#if DEBUG
    if (getenv("runningTests"))
        return YES;
#endif
  • 对于-applicationDidBecomeActive:方法也做同样的操作,但只需使用return

  • 更新:我改变了我的方法。请参见如何轻松切换您的应用委托以进行测试


    1
    我猜这只适用于在-application:didFinishLaunchingWithOptions:中加载其GUI的应用程序。如果您正在构建使用故事板的应用程序,该怎么办? - ABeanSits
    我应该将这段代码放在哪里呢?我的当前目标根本没有AppDelegate。如果我将其添加到我的UnitTest目标中,它也不会有任何变化。(我正在使用XCode 4.6) - Victor Ronin
    @JonReid:好问题。我该如何检查呢?这个目标没有摘要选项卡,当我查看project.pbxproj时,发现productType = "com.apple.product-type.bundle";所以,我假设它构建一个bundle。我需要哪种类型的目标呢? - Victor Ronin
    @VictorRonin 由于您正在开发一个捆绑包,因此这个SO问题不适用于您。要针对您的捆绑包编写单元测试,您需要一个可加载您的捆绑包的单元测试目标。 - Jon Reid
    @JonReid:抱歉让你感到困惑。我的主要目标是一个iOS应用程序。我有第二个目标,其中包含了所有的测试。这是一个bundle。 - Victor Ronin
    显示剩余2条评论

    6
    在最新的xcode版本(5.0.2)中,您可以非常轻松地完成此操作。选择您的测试目标,“常规”选项卡。在“Target”字段中设置“None”。然后点击“Build phases”选项卡,并从“Target dependencies”中删除您的主目标。

    2
    你确定这样就够了吗?我已经尝试了报告的步骤,但它仍然启动模拟器。非常感谢。 - Luca Molteni
    为了修复编译错误,请确保链接了所有测试用例所需的框架(通常与您的 .app 链接相同),并确保您的构建阶段编译了所有测试用例所需的源文件。 - mxcl
    很棒的答案!对于那些这个解决方案不起作用的人:请注意(至少在Xcode 7+中),有两个测试目标:AppNameTests和AppNameUITests。在“编辑方案”->“测试”->“信息”中,这两个测试目标都已启用测试。在此处取消选择“UITests”。如果选择了“UITests”,则会有一个构建阶段会启动模拟器。 - auco

    3
    在你的情况下,我假设你有一个单独的Logic Tests和Application Tests目标(如果没有-你需要)。在你的schemes配置中,你定义了为“Test”方案构建哪些目标。如果你的应用程序测试没有运行,模拟器将不会启动。
    我怀疑你可能正在尝试在“应用程序测试”目标中运行“逻辑测试”(例如Xcode默认创建的一个)。了解更多关于这种区别的信息和如何设置它,请看这里

    2

    注意,未在Xcode 5上进行测试。

    我使用了@jon-reid的答案,但是发现Xcode将环境变量添加到XcodeProjects的xcuserstated部分中,而这些是特定于用户的,通常不会提交到仓库。因此,我用交换方法(swizzle)重写了我的AppDelegate以覆盖其加载:

    @implementation MyAppDelegate (Testing)
    
    + (void)initialize {
        SEL new = @selector(application:didFinishLaunchingWithOptions:);
        SEL orig = @selector(swizzled_application:didFinishLaunchingWithOptions:);
        Class c = [self class];
        Method origMethod = class_getInstanceMethod(c, orig);
        Method newMethod = class_getInstanceMethod(c, new);
    
        if (class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
            class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
        } else {
            method_exchangeImplementations(origMethod, newMethod);
        }
    }
    
    - (BOOL)swizzled_application:(id)app didFinishLaunchingWithOptions:(id)opts {
        return YES;
    }
    
    @end
    

    请注意,以下方法更为简单且有效,但我不确定其可靠性:

    注意,以下方法更为简单且有效,但我不确定其可靠性:

    @implementation MyAppDelegate (Testing)
    
    - (BOOL)application:(id)app didFinishLaunchingWithOptions:(id)opts {
        return YES;
    }
    
    @end
    

    这种方法有效的原因是动态加载组件(如测试包)中方法的类别优先级更高。然而,交换方法可能更加安全。

    我应该把这段代码放在哪里?我的当前目标根本没有AppDelegate。如果我将其添加到我的UnitTest目标中,它也不会改变任何东西。(我正在使用XCode 4.6) - Victor Ronin
    将此代码放入测试目标中,并将 MyAppDelegate 重命名为您主目标中 AppDelegate 类的名称。您的测试目标没有应用程序委托,但这就是这个问题的重点,来自主目标的应用程序委托仍然会被执行。 - mxcl

    2
    早先的回答中指出,逻辑测试是这种情况下正确的做法。我在 XCode 版本 4.3.2 (4E2002) 中遇到了很大的困难,无法使逻辑测试正常运行。查看 Apple 的示例单元测试项目 帮助我理解如何通过明确的分离来实现这一点。在该示例中,逻辑测试测试库目标文件而不是应用程序目标文件。模型被封装到一个库中,然后与主目标和逻辑测试目标链接。应用程序目标仅包含视图和控制器。

    基于这个模型,这就是我为了让我的逻辑测试正常工作所做的事情。创建一个新的目标(Cocoa Touch 静态库),并将所有要进行逻辑测试的文件(通常是您的所有模型)移动到这个新目标中。在"Build Phases"设置中,在应用程序目标和逻辑测试目标的"Link Binary With Libraries"中添加这个新库。

    我可以想象这些说明可能有点令人困惑。如果你剖析上面提到的示例项目,你会更好地理解。


    1

    使用xCode 7和xctool

    xctool能够在没有模拟器的情况下执行单元测试。

    要使其正常工作,

    1. 更新目标设置以无主机应用程序运行。

    选择你的项目 --> 然后测试目标 --> 将主机应用程序设置为“无”。

    enter image description here

    安装xctool,如果你没有它。
    brew install xctool
    

    使用终端和xctool运行测试。
     xctool -workspace yourWorkspace.xcworkspace -scheme yourScheme run-tests -sdk iphonesimulator
    

    0

    我使用GHUnit创建了适用于OSX/iOS的测试套件。虽然存在一些问题,但我发现它比OCUnit更可靠/兼容/简单。

    GHUnit为OS X和iOS提供基本的模板项目,使初始设置变得简单。

    注意:通常我只使用自己的工具包进行大部分测试。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接