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

时间:2011-03-23 10:42:52

标签: xcode unit-testing xcode4 xcodebuild ocunit

我在Xcode 4中创建了一个全新的iOS项目,并包含单元测试。默认应用程序有2个目标,主应用程序和单元测试包。使用“Product> Test”(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消除了这种区别。我是如何从命令行运行我的测试的?

5 个答案:

答案 0 :(得分:48)

重要提示

使用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 Keychain,当在恶意攻击Apple脚本的环境中运行时,这些调用因错误而失败(errSecNotAvailable [ - 25291]对于病态的好奇)。结果,测试总是失败......测试中的一个不良特征。

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

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

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

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

但是,不是黑客攻击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 grunge将它们连接成ios-sim应用程序的一系列命令行参数。

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

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

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

答案 1 :(得分:9)

我的灵感来自约拿的帖子并找到了一种方法:

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

基本上,你需要Xcode 4,你必须破解一个脚本才能使它工作,但确实如此。

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

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

答案 2 :(得分:8)

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

xcodebuild  -target MyTarget -sdk iphonesimulator   TEST_AFTER_BUILD=YES

答案 3 :(得分:5)

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

答案 4 :(得分:3)

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

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