【问题标题】:Xcode 4: Run tests from the command line (xcodebuild)?Xcode 4:从命令行(xcodebuild)运行测试?
【发布时间】:2011-07-21 05:06:02
【问题描述】:

我在 Xcode 4 中创建了一个全新的 iOS 项目,并包含了单元测试。默认应用程序有 2 个目标,主应用程序和单元测试包。使用“产品 > 测试”(Command-U)构建应用程序,构建单元测试包,启动 iOS 模拟器并运行测试。现在我希望能够从命令行做同样的事情。命令行工具(xcodebuild)没有“测试”操作,但似乎我应该能够直接构建单元测试包目标,因为它取决于应用程序本身。但是,运行:

xcodebuild -target TestAppTests -sdk iphonesimulator4.3 -configuration Debug build

给出以下信息:

/Developer/Platforms/iPhoneSimulator.platform/Developer/Tools/Tools/RunPlatformUnitTests:95: warning: Skipping tests; the iPhoneSimulator platform does not currently support application-hosted tests (TEST_HOST set).

这似乎是个谎言,因为当我从 GUI 运行 Command-U 时,我的单元测试包目标设置了测试主机。我以前看过关于逻辑测试和应用程序测试之间分离的帖子,但似乎 Xcode 4 消除了这种区别。知道如何从命令行运行测试吗?

【问题讨论】:

    标签: xcode unit-testing xcode4 xcodebuild ocunit


    【解决方案1】:

    重要提示

    对于 Xcode 5.1(可能还有早期的 Xcode)test 是一个有效的构建操作。

    我们能够使用 test 的构建操作和适当的 -destination 选项调用 xcodebuild 来替换下面的整个 hack。 man xcodebuild 了解更多信息。

    以下信息留作后人


    我尝试破解 Apple 的脚本来运行

    中提到的单元测试

    Running Xcode 4 unit tests from the command line

    Xcode4: Running Application Tests From the Command Line in iOS

    以及网络上许多类似的帖子。

    但是,我遇到了这些解决方案的问题。我们的一些单元测试使用了 iOS 钥匙串,当在来自黑客 Apple 脚本的环境中运行时,这些调用因错误而失败(对于病态的好奇,errSecNotAvailable[-25291])。结果,测试总是失败......测试中的一个不受欢迎的功能。

    我根据在网络上其他地方找到的信息尝试了多种解决方案。例如,其中一些解决方案涉及尝试启动 iOS 模拟器的安全服务守护进程。在与这些斗争之后,我最好的选择似乎是在 iOS 模拟器中运行,充分利用模拟器环境。

    我做了什么,然后得到了 iOS 模拟器启动工具 ios-sim。此命令行工具使用私有 Apple 框架从命令行启动 iOS 应用程序。然而,对我来说特别有用的是它允许我将环境变量和命令行参数传递给它正在启动的应用程序。

    通过环境变量,我能够将单元测试包注入到我的应用程序中。通过命令行参数,我可以传递让应用程序运行单元测试并退出所需的“-SenTest All”。

    我为我的单元测试包创建了一个方案(我称之为“CommandLineUnitTests”)并检查了构建部分中的“运行”操作,如上述帖子中所述。

    不过,我没有破解 Apple 的脚本,而是将脚本替换为使用 ios-sim 启动应用程序并设置环境以将我的单元测试包单独注入应用程序的脚本。

    我的脚本是用比 BASH 脚本更熟悉的 Ruby 编写的。这是那个脚本:

    if ENV['SL_RUN_UNIT_TESTS'] then
        launcher_path = File.join(ENV['SRCROOT'], "Scripts", "ios-sim")
        test_bundle_path= File.join(ENV['BUILT_PRODUCTS_DIR'], "#{ENV['PRODUCT_NAME']}.#{ENV['WRAPPER_EXTENSION']}")
    
        environment = {
            'DYLD_INSERT_LIBRARIES' => "/../../Library/PrivateFrameworks/IDEBundleInjection.framework/IDEBundleInjection",
            'XCInjectBundle' => test_bundle_path,
            'XCInjectBundleInto' => ENV["TEST_HOST"]
        }
    
        environment_args = environment.collect { |key, value| "--setenv #{key}=\"#{value}\""}.join(" ")
    
        app_test_host = File.dirname(ENV["TEST_HOST"])
        system("#{launcher_path} launch \"#{app_test_host}\" #{environment_args} --args -SenTest All #{test_bundle_path}")
    else
        puts "SL_RUN_UNIT_TESTS not set - Did not run unit tests!"
    end
    

    从命令行运行如下:

    xcodebuild -sdk iphonesimulator -workspace iPhoneApp.xcworkspace/ -scheme "CommandLineUnitTests" clean build SL_RUN_UNIT_TESTS=YES
    

    在查找SL_RUN_UNIT_TESTS 环境变量后,脚本会在项目的源代码树中找到“启动器”(iOS-sim 可执行文件)。然后它会根据 Xcode 在环境变量中传递的构建设置来构建我的单元测试包的路径。

    接下来,我为正在运行的应用程序创建一组运行时环境变量,以注入单元测试包。我在脚本中间的 environment 哈希中设置了这些变量,然后使用一些 ruby​​ grunge 将它们加入到ios-sim 应用程序的一系列命令行参数中。

    在底部附近,我从环境中抓取TEST_HOST 作为我要启动的应用程序,system 命令实际上执行ios-sim,传递应用程序、设置环境的命令参数和参数@ 987654337@ 和正在运行的应用程序的测试包路径。

    这个方案的优点是它在模拟器环境中运行单元测试,就像我相信 Xcode 本身一样。该方案的缺点是它依赖于外部工具来启动应用程序。该外部工具使用私有的 Apple 框架,因此在随后的操作系统版本中它可能会很脆弱,但目前可以使用。

    附:出于叙述原因,我在这篇文章中经常使用“我”,但很多功劳归功于我的犯罪伙伴 Pawel,他与我一起解决了这些问题。

    【讨论】:

    • 顺便提一下,DYLD_INSERT_LIBRARIES 在我的脚本中与从 IDE 启动应用程序的测试操作时在应用程序环境中的相对路径相同。我相信这条路径是相对于另一个环境变量“DYLD_ROOT”的。只是不希望它看起来有很深的魔法。
    • Scott,是的,我的钥匙串也有类似的问题,但我不需要深入到这些深度来修复它。你看到这个帖子了吗:stackoverflow.com/questions/9996578/…
    • 这个答案应该被接受,适用于 XCode 4.5,也适用于 Jenkins!感谢分享。 bigkm 的答案只会运行逻辑单元测试,如果您构建工作空间和方案而不是目标,则无法应用。
    • Miroslav,是的,我确实遇到了同样的问题,但它有助于关闭之前似乎是从 Xcode 启动的 iOS 模拟器。
    • @ScottThompson 您是如何使用 Ruby 脚本的?它必须替换 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Tools/RunPlatformUnitTests?它必须被它调用?我对它的来源有点迷茫。
    【解决方案2】:

    xctool 解决了这个问题:https://github.com/facebook/xctool

    我们在持续集成服务器上使用它没有问题

    【讨论】:

    • 我正在尝试xctool,但仍然收到这篇文章的错误信息。您使用的是哪个版本的 Xcode?​​span>
    • @meaning-matters,他们的发行说明显示 2013 年是它的第一个版本,并且更改表明 xcode5 是第一个受支持的版本。它仍在积极开发中,看起来像一个不错的 api。 Ty skrusche 作为提示,我来这里是为了 xcode 6,这非常有帮助。 github.com/facebook/xctool/releases
    【解决方案3】:

    您正在寻找的是这个未记录的参数(您也需要 sdk 和 target)从终端运行您的 OCUnit 测试

    xcodebuild  -target MyTarget -sdk iphonesimulator   TEST_AFTER_BUILD=YES
    

    【讨论】:

    • 这适用于我的 Xcode 4.4.1 和 4.5。它在 4.3 上不起作用,但这可能是由于一个不相关的问题。
    • 这仅运行逻辑单元测试,如果您构建工作区而不是目标,则可能会出现问题。在我的项目中,我有工作空间来处理依赖关系,所以我真的不可能切换到构建目标。
    • 请注意,这不是一个未记录的参数,而是您正在设置的环境变量。
    • 当我运行 xcodebuild -target TestTarget -sdk iphonesimulator TEST_AFTER_BUILD=YES 时,我使用 XCode 4.6.1 得到 warning: Skipping tests; the iPhoneSimulator platform does not currently support application-hosted tests (TEST_HOST set).
    【解决方案4】:

    我受到 Jonah 的帖子的启发,并找到了一种方法:

    http://longweekendmobile.com/2011/04/17/xcode4-running-application-tests-from-the-command-line-in-ios/

    基本上,您需要 Xcode 4 并且必须编写一个脚本才能使其工作,但它确实可以。

    关键是要说服 Xcode 4 像运行 MacOS X 包一样运行您的 iOS 测试包 - 这是平台的问题,Xcode 不想在命令行上运行开箱即用的应用程序测试。有趣,因为它似乎有效。

    网站上还有一个示例项目。

    【讨论】:

    • 我刚刚尝试了@makdad 的解决方案,它对我有用。现在肯定有一种更简单的方法来运行命令行测试,而无需破解 /Developer 脚本?
    • 如果你能找到,我会链接到你的博客! :)
    • 有关于这个问题的消息吗?我仍然对从命令行运行应用程序测试而不侵入 /Developer 感兴趣。我第一次尝试 Xcode 4.2 似乎遇到了同样的问题。
    • @otto,还没有:) 我博客的 cmets 上的人一直在说,从 Xcode 4.2 开始,hack 仍然是必要的。
    • 我没有破解 PLATFORM_DEVELOPER_TOOLS_DIR 中的脚本。相反,我将它复制到我的 repo 中,在那里对其进行了调整并创建了一个包装脚本。但是,是的,这很烦人。
    【解决方案5】:

    这是一个不完整的解决方案,但我能够在他们自己的方案中运行逻辑测试的命令行构建并构建目标:http://blog.carbonfive.com/2011/04/06/running-xcode-4-unit-tests-from-the-command-line/

    【讨论】:

    • 该页面上的说明对我有用,我不需要更改 /Developer 中的任何内容。谢谢!
    • Bob,你使用的是哪个版本的 iOS?或者更具体地说 - Xcode?​​span>
    • @BobWhiteman,如果你只是在运行逻辑测试,那么这个 hack 就没有必要了——可能是你的情况,不是吗?
    • 我的配置与网站完全相同,但无法运行测试。 LogicTest 方案构建成功,但正在运行任何测试。
    猜你喜欢
    • 2016-10-13
    • 2013-12-12
    • 1970-01-01
    • 1970-01-01
    • 2018-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-30
    相关资源
    最近更新 更多