47

我在 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 消除了这种区别。有什么线索可以从命令行运行我的测试吗?

4

5 回答 5

48

重要的提示

使用 Xcode 5.1(也许早期的 Xcode 也是如此)test是一个有效的构建操作。

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

以下信息留作后人


我尝试破解 Apple 的脚本来运行单元测试,如

从命令行运行 Xcode 4 单元测试

Xcode4:在 iOS 中从命令行运行应用程序测试

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

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

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

我所做的就是使用 iOS Simulator 启动工具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传递应用程序、设置环境的命令参数-SenTest All以及正在运行的应用程序的参数和测试包路径。

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

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

于 2012-05-30T20:06:28.127 回答
9

我受到 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 不想在命令行上运行开箱即用的应用程序测试。有趣,因为它似乎有效。

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

于 2011-04-17T19:42:50.477 回答
8

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

xcodebuild  -target MyTarget -sdk iphonesimulator   TEST_AFTER_BUILD=YES
于 2012-09-10T06:26:34.413 回答
5

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

于 2011-04-06T20:45:09.953 回答
3

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

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

于 2013-06-21T06:46:13.800 回答