Xcode 4:如何从命令行运行测试(xcodebuild)?

47

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

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

5个回答

48

重要提示

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

我们能够通过调用带有适当的-destination选项的xcodebuild的构建操作来替换下面的整个hack。更多信息请参见man xcodebuild

以下信息留在此处供后人参考


我试图黑客攻击Apple的脚本以运行单元测试,如下所述

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

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

以及互联网上的类似帖子不计其数。

但是,我在这些解决方案中遇到了一个问题。我们的一些单元测试使用了iOS Keychain,当在来自黑客攻击Apple脚本的环境中运行那些调用时,会出现错误(对于好奇心者,是errSecNotAvailable [-25291])。因此,测试始终失败......这是一个测试中不希望出现的特性。

我尝试了许多基于我在互联网上找到的信息的解决方案。其中一些解决方案涉及尝试启动iOS模拟器的安全服务守护程序,例如。在苦苦挣扎后,我的最佳选择似乎是在iOS模拟器中运行,并充分利用模拟器的环境。

然后我获取了iOS模拟器启动工具ios-sim。这个命令行工具使用专有的Apple框架从命令行启动iOS应用程序。对我特别有用的是,它允许我将环境变量和命令行参数传递给它正在启动的应用程序。

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

我为我的单元测试捆绑包创建了一个方案(我称之为"CommandLineUnitTests"),并在构建部分中勾选了"Run"操作,如上述帖子中所述。

不过,我并没有黑客攻击Apple的脚本,而是用一个脚本替换了该脚本,该脚本使用ios-sim启动应用程序,并设置环境以单独将我的单元测试捆绑包注入应用程序中。

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

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技巧将它们连接成一系列命令行参数,以供ios-sim应用程序使用。

接近底部,我从环境中获取了TEST_HOST作为我要启动的应用程序,system命令实际上执行了ios-sim,传递应用程序、设置环境的命令参数和参数-SenTest All以及测试捆绑包路径到正在运行的应用程序。

这种方案的优点是它在模拟器环境中运行单元测试,与我认为Xcode本身的方式非常相似。该方案的缺点是它依赖于外部工具来启动应用程序。该外部工具使用私有的Apple框架,因此可能会因为随后的操作系统发布而变得脆弱,但目前它可以工作。

附言:我在本帖中经常使用“我”这个词进行叙述,但很多功劳归于我的伙伴Pawel,他和我一起解决了这些问题。


@bigkm的答案对我很有用,而且更简单。这可能取决于Xcode的版本。 - ThomasW
2
这个答案应该被接受,在XCode 4.5和Jenkins中都可以工作!感谢分享。bigkm的答案只能运行逻辑单元测试,如果你构建一个工作区和方案而不是目标,则无法应用。 - lawicko
只是一个更新 - 这在Xcode 4.5中有效,我将更新上面提到的博客文章,以便为这个解决方案贡献一些功劳,因为很多人来到我的博客寻找解决方案... - makdad
1
Miroslav,是的,我也遇到了同样的问题,但关闭之前从Xcode启动的iOS模拟器有所帮助。 - Ciryon
6
你是如何精确使用Ruby脚本的?它是否替换了/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Tools/RunPlatformUnitTests?它需要由此调用吗?我有点不清楚它在哪里发挥作用。 - Maciej Trybiło
显示剩余11条评论

9
我受Jonah的帖子启发,找到了一种方法来实现这个问题:http://longweekendmobile.com/2011/04/17/xcode4-running-application-tests-from-the-command-line-in-ios/。基本上,你需要Xcode 4,并且必须修改一个脚本才能使其正常工作,但它确实可以解决问题。关键是要让Xcode 4将你的iOS测试包视为MacOS X包运行——这是平台的问题,Xcode默认不支持在命令行上运行应用程序测试。有趣的是,它似乎可以正常工作。该网站上还有一个示例项目。

如果你能找到它,我会链接到你的博客! :) - makdad
有关这个问题有任何消息吗?我仍然对在命令行中运行应用程序测试而无需侵入/ Developer感兴趣。 我尝试使用Xcode 4.2,但似乎遇到了同样的问题。 - otto
@otto,还没有 :) 我博客上的评论中有人说从Xcode 4.2开始,这个黑客仍然是必需的。 - makdad
我没有黑掉PLATFORM_DEVELOPER_TOOLS_DIR中的脚本。相反,我将其复制到我的存储库中,在那里进行了调整并创建了一个包装脚本。但是,确实很烦人需要这样做。 - user23743
@bigkm的答案对我很有用,而且更简单。这可能取决于Xcode的版本。 - ThomasW
显示剩余2条评论

8
你需要的是这个未记录的参数(你也需要sdk和target),以便从终端运行你的OCUnit测试。
xcodebuild  -target MyTarget -sdk iphonesimulator   TEST_AFTER_BUILD=YES

这对我来说在Xcode 4.4.1和4.5上有效。在4.3上它不起作用,但这可能是由于一个无关的问题。 - ThomasW
3
仅运行逻辑单元测试可能会存在问题,如果您构建的是工作区而不是目标。在我的项目中,工作区负责处理依赖关系,因此我无法轻易地切换到构建目标。 - lawicko
2
顺便提一下,这不是一个未记录的参数,而是您正在设置的环境变量。 - makdad
1
当我运行 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). 的警告。 - aledalgrande

5

那个页面上的指示对我很有用,而且我不需要更改/Developer中的任何内容。谢谢! - Bob Whiteman
鲍勃,你用的是哪个版本的iOS?或者更具体地说,是Xcode? - makdad
@BobWhiteman,如果你只是在运行逻辑测试,那么这个hack就不是必需的——可能是你的情况,对吧? - makdad
我按照网站的配置进行了精确设置,但是无法运行测试。LogicTest方案构建成功,但没有运行任何测试。 - Raphael Oliveira

3

我正在尝试使用 xctool,但仍然收到了这篇帖子所描述的错误信息。您正在使用哪个版本的 Xcode? - meaning-matters
@meaning-matters,他们的发布说明显示2013年是其第一个版本的发布日期,更改内容表明xcode5是第一个受支持的版本。它仍在积极开发中,并且看起来是一个不错的API。感谢skrusche的提示,我来这里找xcode 6,这非常有帮助。https://github.com/facebook/xctool/releases - AnneTheAgile

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