重要的提示
使用 Xcode 5.1(也许还有更早的 Xcode)test
是一个有效的构建操作。
我们能够使用 test 的构建操作并使用适当的方法通过调用 xcodebuild 来替换下面的整个 hack-destination
选项。man xcodebuild
了解更多信息。
以下信息留在这里供后代使用
我尝试破解苹果的脚本来运行单元测试,如中所述
从命令行运行 Xcode 4 单元测试 http://blog.carbonfive.com/2011/04/06/running-xcode-4-unit-tests-from-the-command-line/
and
Xcode4:在 iOS 中从命令行运行应用程序测试 http://longweekendmobile.com/2011/04/17/xcode4-running-application-tests-from-the-command-line-in-ios/
以及网络上无数类似的帖子。
但是,我在这些解决方案中遇到了问题。我们的一些单元测试使用了 iOS 钥匙串,当在来自破解 Apple 脚本的环境中运行时,这些调用失败并出现错误(errSecNotAvailable
[-25291] 对于病态的好奇者)。结果,测试总是失败……这是测试中不受欢迎的功能。
我根据在网络上其他地方找到的信息尝试了多种解决方案。例如,其中一些解决方案涉及尝试启动 iOS 模拟器的安全服务守护进程。在与这些问题作斗争之后,我最好的选择似乎是在 iOS 模拟器中运行,并充分利用模拟器环境的优势。
然后我所做的就是找到 iOS Simulator 启动工具ios-sim https://github.com/pegli/ios-sim。该命令行工具使用私有 Apple 框架从命令行启动 iOS 应用程序。然而,对我来说特别有用的是,它允许我将环境变量和命令行参数传递给它正在启动的应用程序。
通过环境变量,我能够将单元测试包注入到我的应用程序中。通过命令行参数,我可以传递让应用程序运行单元测试并退出所需的“-SenTest All”。
我为我的单元测试包创建了一个方案(我称之为“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
hash 在脚本中间,然后使用一些 ruby grunge 将它们加入到一系列命令行参数中ios-sim
应用。
在底部附近我抓住了TEST_HOST
从环境中作为我想要启动的应用程序和system
命令实际执行ios-sim
传递应用程序、设置环境的命令参数以及参数-SenTest All
以及正在运行的应用程序的测试包路径。
这个方案的优点是它在模拟器环境中运行单元测试,就像我相信 Xcode 本身所做的那样。该方案的缺点是它依赖于外部工具来启动应用程序。该外部工具使用私有 Apple 框架,因此在后续操作系统版本中它可能会很脆弱,但目前可以使用。
附:出于叙述原因,我在这篇文章中多次使用“我”,但很大程度上要归功于我的犯罪伙伴 Pawel,他和我一起解决了这些问题。