Xcode 4 : Run tests from the command line (xcodebuild)?

标签 xcode unit-testing xcode4 xcodebuild ocunit

我在 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 5.1(可能还有早期的 Xcode)test是一个有效的构建 Action 。

我们能够使用 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 .此命令行工具使用私有(private) Apple 框架从命令行启动 iOS 应用程序。然而,对我来说特别有用的是它允许我将环境变量和命令行参数传递给它正在启动的应用程序。

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

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

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

我的脚本是用比 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 在环境变量中传递的build设置构建我的单元测试包的路径。

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

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

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

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

关于Xcode 4 : Run tests from the command line (xcodebuild)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5403991/

相关文章:

c++ - 字符串以某个字符串开头

ios - 如何在 Swift 4 中向 UIView 添加 subview ?

ios - 合并两个 iOS 应用程序

c++ - catch单元测试框架支持junit风格的测试报告吗?

c++ - 在同一项目中编译 C++ 和 ObjC 文件

python - 我如何使用 python 和 xcode 4.2 中的界面生成器?

unit-testing - 使用包含 Autowiring 属性的 ControllerAdvice 进行 WebMvc 测试

objective-c - 可以使用 Class 对象来转换 Objective-C 对象吗?

iphone - XCode 4 对 .h 文件中方法的代码完成

objective-c - 与 scanf 函数一起使用