【问题标题】:How the ios app "knows" to run the unit testsios 应用程序如何“知道”运行单元测试
【发布时间】:2014-09-16 00:43:51
【问题描述】:

我知道我可以使用 xcodebuild 开始我的应用程序的单元测试,但我想知道是什么告诉应用程序在启动期间运行测试,它是发送给应用程序的特殊参数还是它以不同方式编译以便运行测试(使用 XCTest)?

【问题讨论】:

标签: ios objective-c unit-testing xcode5 xctest


【解决方案1】:

我知道它是如何与 OCTest 一起工作的,我想 XCTest 的工作方式类似。

我曾经使用WaxSim 运行我的测试,这是一个访问私有模拟器框架的实用程序。 WaxSim 的使用在这里并不重要,它只是让我们能够检查内部。

运行命令在模拟器中运行MyApp.app。要开始测试,首先你必须注入它们(它们被编译成一个单独的包!)。命令看起来像这样

WaxSim \
    -e DYLD_INSERT_LIBRARIES="$INJECTION_FRAMEWORK_PATH" \
    -e XCInjectBundle="MyApp.octest" \
    -e XCInjectBundleInto="MyApp.app/MyApp" \
    MyApp.app \
   -SenTest All

INJECTION_FRAMEWORK_PATH 的定义如下:

XCODE_PATH=$( xcode-select --print-path )
PLATFORM_PATH="$XCODE_PATH/Platforms/iPhoneSimulator.platform"
INJECTION_FRAMEWORK_PATH="$PLATFORM_PATH/Developer/Library/PrivateFrameworks/IDEBundleInjection.framework/IDEBundleInjection"

还要注意-SenTest All 命令行参数,这些参数告诉框架要运行哪些测试。

基本上,应用程序像往常一样运行,但是注入了另一个包来逐个运行测试,然后退出整个应用程序。

【讨论】:

    【解决方案2】:

    Xcode 使用xctestRunTargetUnitTests 脚本(来自/Developer/Tools/RunTargetUnitTests)进行单元测试。

    xctestdynamic library(具有单独线程用于其作业的测试工具包)注入您的二进制文件,注入使dylib 访问您的进程内存(它可以访问您进程内存中的类/实例),它能够执行调用和进行单元测试。来自设备/模拟器的回调是从调试器接收的(没有特殊的单元测试技术)。

    简单地说:test scheme的项目照常编译,但它链接的动态库会木马你的进程内存,并进行测试。

    还有非常有用的信息:

    RunUnitTests 接受ENVIRONMENT variables,这里有一些有趣的

    TEST_HOST - 要“注入”的可执行文件的完整路径 指定的单元测试包。对于应用程序,这必须是完整的 应用程序在其包装器中的路径。不要为框架设置这个 作品或图书馆。

    TEST_RIG - 可执行文件的完整路径以用作测试装备 CPlusTestRig 或 otest。可执行文件必须采用路径 到一个测试包作为它的最后一个参数。它的 DYLD_FRAMEWORK_PATH 和 DYLD_LIBRARY_PATH 将被配置为指向 BUILT_PRODUCTS_DIR 在执行之前。如果您使用的是默认值之一,请不要设置此项 试验台。


    BUNDLE_LOADER 用作链接器选项,表示链接器将Testing dynamic library 链接到您指定的二进制文件中。

    测试包目标模板的最后有一个 shell 脚本构建阶段,它调用 /Developer/Tools/RunUnitTests。 RunUnitTests 查看通过其环境传递的构建设置,并根据该信息确定如何在测试包中运行测试。

    如果您正在测试一个框架,RunUnitTests 将运行适当的测试装备并告诉它加载和运行您的包中的测试。由于您的测试包应该链接到您的框架,因此您的框架将在测试平台加载您的包时加载。

    如果您正在测试应用程序,则需要在其配置的构建设置中将该应用程序指定为测试包的测试主机和包加载器。 Bundle Loader 设置告诉链接器将您的包链接到正在加载它的应用程序,就好像应用程序是一个框架一样,允许您从包中引用应用程序中的类和其他符号,而无需实际将它们包含在包中。测试主机设置告诉 RunUnitTests 启动指定的应用程序并将您的测试包注入其中以运行其测试。

    有关更多信息,请参阅man 页的RunTargetUnitTests/xctest

    https://developer.apple.com/library/mac/documentation/Darwin/Reference/Manpages/man1/RunTargetUnitTests.1.html#//apple_ref/doc/man/1/RunTargetUnitTests

    https://developer.apple.com/library/mac/documentation/Darwin/Reference/Manpages/man1/xctest.1.html

    这是RunTargetUnitTests 脚本

    #!/bin/sh # # 版权所有 (c) 2005-2013 Apple Inc. 保留所有权利。 # # 版权所有 (c) 1997-2005,Sen:te (Sente SA)。版权所有。 # # 此源代码的使用受以下许可的约束: # # 以源代码和二进制形式重新分发和使用,无论是否修改, # 是允许的,前提是满足以下条件: # # (1) 再分发源代码必须保留以上版权声明, # 此条件列表和以下免责声明。 # # (2) 以二进制形式再分发必须复制上述版权声明, # 此条件列表和文档中的以下免责声明 # 和/或随分发提供的其他材料。 # # 本软件由版权所有者和贡献者“按原样”提供 # 以及任何明示或暗示的保证,包括但不限于暗示的保证 # 对适销性和特定用途适用性的保证不予承认。 # 在任何情况下,Sente SA 或贡献者均不对任何直接、间接、附带、 # 特殊、惩戒性或后果性损害(包括但不限于采购 # 替代商品或服务;使用、数据或利润的损失;或业务中断) # 无论是出于何种原因和基于任何责任理论,无论是合同责任还是严格责任, # 或以任何方式因使用本软件而产生的侵权行为(包括疏忽或其他), # 即使被告知此类损害的可能性。 # # 注意:此许可证等同于 FreeBSD 许可证。 # # 此通知可能不会从该文件中删除。 如果 [ "${NATIVE_ARCH_ACTUAL}" = "" ];然后 NATIVE_ARCH_ACTUAL=`拱` 菲 如果 [ "${ARCHS}" = "" ];然后 ARCHS=`拱` 菲 如果 [ "${DEVELOPER_DIR}" = "" ];然后 DEVELOPER_DIR="${SYSTEM_DEVELOPER_DIR}" 菲 如果 [ "${OTEST}" = "" ];然后 OTEST="${DEVELOPER_DIR}/Tools/otest" 菲 如果 [ "${OTEST_TARGET}" = "" ];然后 OTEST_TARGET="${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}" 菲 RunTargetUnitTestsForArch() { echo "${DEVELOPER_DIR}/Tools/RunTargetUnitTests:0: 注意:开始测试 ${1}" 如果 [ "${DYLD_FRAMEWORK_PATH}" = "" ] ;然后 DYLD_FRAMEWORK_PATH="${BUILT_PRODUCTS_DIR}:${DEVELOPER_DIR}/Library/Frameworks" 别的 DYLD_FRAMEWORK_PATH="${BUILT_PRODUCTS_DIR}:${DEVELOPER_DIR}/Library/Frameworks:${DYLD_FRAMEWORK_PATH}" 菲 导出 DYLD_FRAMEWORK_PATH 回声“OTEST=${OTEST}” 拱 -arch "${1}" "${OTEST}" "${OTEST_TARGET}" 取消设置 DYLD_FRAMEWORK_PATH echo "${DEVELOPER_DIR}/Tools/RunTargetUnitTests:0: 注意:${1} 的已完成测试" } SkipTargetUnitTestsForArch() { echo "${DEVELOPER_DIR}/Tools/RunTargetUnitTests:0: 注意:跳过 ${1} 的测试" } 如果 [ "${TEST_AFTER_BUILD}" = "YES" ];然后 # 每个请求和支持的架构运行一次单元测试。 ${ARCHS} 中的 TEST_ARCH;做 案例“${NATIVE_ARCH_ACTUAL}”在 i386) 如果 [ "${TEST_ARCH}" = "i386" ];然后 RunTargetUnitTestsForArch "${TEST_ARCH}" 别的 SkipTargetUnitTestsForArch "${TEST_ARCH}" 菲 ;; x86_64) 如果 [ "${TEST_ARCH}" = "i386" -o "${TEST_ARCH}" = "x86_64" ];然后 RunTargetUnitTestsForArch "${TEST_ARCH}" 别的 SkipTargetUnitTestsForArch "${TEST_ARCH}" 菲 ;; *) RunTargetUnitTestsForArch "${TEST_ARCH}" ;; 经社理事会 完毕 菲

    【讨论】:

    • 这很有趣。但是我没有看到xctest/otest 是如何被RunTargetUnitTests 脚本调用的,也没有看到任何dylib 注入。
    • @Droppy 这是一个错误(我已经更新了帖子),dynamic libraries 是通过otest/xctest 工具注入的。 otest/xctest 正在从 Xcode 调用。
    • 请将TEST_RIGTEST_HOSTBUNDLE_LOADER 编译设置添加到您的答案中,您将获得我的支持。)
    • 我认为 iOS 根本不会调用 otestxctest
    • RunUnitTests 脚本已在 Xcode 7 中弃用:RunUnitTests is obsolete. To run unit tests for your target, use the Test scheme action in the Xcode IDE and the test action in xcodebuild.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-12
    • 2015-02-14
    • 2016-03-23
    • 1970-01-01
    相关资源
    最近更新 更多