简体   繁体   English

ios app如何“知道”运行单元测试

[英]How the ios app “knows” to run the unit tests

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

Xcode uses xctest and RunTargetUnitTests script (from /Developer/Tools/RunTargetUnitTests ) for unit testing. Xcode使用xctestRunTargetUnitTests脚本(来自/Developer/Tools/RunTargetUnitTests )进行单元测试。

xctest injects dynamic library (testing kit which has separate thread for it's jobs) into your binary, injection gives the dylib access to your process memory (it can access your Classes/Instances in you process memory), and it's able to perform calls and make unit tests. xctestdynamic library (测试工具包具有单独的线程作为其作业)注入二进制文件,注入使dylib访问您的进程内存(它可以访问您的进程内存中的类/实例),并且它能够执行调用和make单元测试。 Callbacks from device/simulator are received from debugger(no special technique for unit testing). 从调试器接收来自设备/模拟器的回调(没有用于单元测试的特殊技术)。

Simply said: The project with test scheme is compiled as usual, but it's linking dynamic library which will trojan your process memory, and make tests. 简单地说:带有test scheme的项目像往常一样编译,但是它会链接动态库,它会使你的进程内存变成木马并进行测试。

Also info that will be very helpful: 还有非常有用的信息:

RunUnitTests accepts ENVIRONMENT variables , here are some interesting of them RunUnitTests接受ENVIRONMENT variables ,这里有一些有趣的ENVIRONMENT variables

TEST_HOST - The full path to an executable into which to "inject" the specified unit test bundle. TEST_HOST - 可“注入”指定单元测试包的可执行文件的完整路径。 For an application, this must be the full path to the application within its wrapper. 对于应用程序,它必须是其包装器中应用程序的完整路径。 Do not set this for frame works or libraries. 不要为框架作品或库设置此项。

TEST_RIG - The full path to an executable to use as a test rig instead of either CPlusTestRig or otest. TEST_RIG - 可用作测试装备而不是CPlusTestRig或otest的可执行文件的完整路径。 The executable must take a path to a test bundle as its final argument. 可执行文件必须将测试包的路径作为其最终参数。 Its DYLD_FRAMEWORK_PATH and DYLD_LIBRARY_PATH will be configured to point to the BUILT_PRODUCTS_DIR prior to execution. 其DYLD_FRAMEWORK_PATH和DYLD_LIBRARY_PATH将配置为在执行之前指向BUILT_PRODUCTS_DIR。 Do not set this if you are using one of the default test rigs. 如果您使用其中一个默认测试装备,请不要设置此项。

also
BUNDLE_LOADER is used as a linker option, which indicates linker to link Testing dynamic library into your specified binary. BUNDLE_LOADER用作链接器选项,指示将Testing dynamic library链接到指定二进制文件的链接器。

The test bundle target templates have a shell script build phase at the very end that invokes /Developer/Tools/RunUnitTests . 测试包目标模板在最后调用/Developer/Tools/RunUnitTests的shell脚本构建阶段。 RunUnitTests looks at the build settings it's passed via its environment and determines from that information how to run the tests in your test bundle. RunUnitTests查看它通过其环境传递的构建设置,并根据该信息确定如何在测试包中运行测试。

If you're testing a framework, RunUnitTests will run the appropriate test rig and tell it to load and run the tests in your bundle. 如果您正在测试框架,RunUnitTests将运行相应的测试装备并告诉它加载并运行捆绑中的测试。 Since your test bundle should link against your framework, your framework will be loaded when the test rig loads your bundle. 由于您的测试包应链接到您的框架,因此当测试装置加载您的包时,您的框架将被加载。

If you're testing an application, you need to specify the application as the Test Host and Bundle Loader for your test bundle in its configuration's build settings. 如果您正在测试应用程序,则需要在其配置的构建设置中将应用程序指定为测试包的测试主机和分发包加载器。 The Bundle Loader setting tells the linker to link your bundle against the application that's loading it as if the application were a framework, allowing you to refer to classes and other symbols within the application from your bundle without actually including them in the bundle. Bundle Loader设置告诉链接器将您的bundle与正在加载它的应用程序链接,就像应用程序是一个框架一样,允许您从bundle中引用应用程序中的类和其他符号,而不实际将它们包含在bundle中。 The Test Host setting tells RunUnitTests to launch the specified application and inject your test bundle into it in order to run its tests. “测试主机”设置告诉RunUnitTests启动指定的应用程序并将测试包注入其中以运行其测试。

For more information see man pages of RunTargetUnitTests / xctest 有关更多信息,请参阅RunTargetUnitTests / xctest man

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/RunTargetUnitTests.1.html#//apple_ref/doc/man/1/RunTargetUnitTests

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

Here is the RunTargetUnitTests script 这是RunTargetUnitTests脚本

#!/bin/sh

#
# Copyright (c) 2005-2013 Apple Inc.  All rights reserved.
#

# Copyright (c) 1997-2005, Sen:te (Sente SA).  All rights reserved.
#
# Use of this source code is governed by the following license:
# 
# Redistribution and use in source and binary forms, with or without modification, 
# are permitted provided that the following conditions are met:
# 
# (1) Redistributions of source code must retain the above copyright notice, 
# this list of conditions and the following disclaimer.
# 
# (2) Redistributions in binary form must reproduce the above copyright notice, 
# this list of conditions and the following disclaimer in the documentation 
# and/or other materials provided with the distribution.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
# IN NO EVENT SHALL Sente SA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# Note: this license is equivalent to the FreeBSD license.
# 
# This notice may not be removed from this file.

if [ "${NATIVE_ARCH_ACTUAL}" = "" ]; then
    NATIVE_ARCH_ACTUAL=`arch`
fi

if [ "${ARCHS}" = "" ]; then
    ARCHS=`arch`
fi

if [ "${DEVELOPER_DIR}" = "" ]; then
    DEVELOPER_DIR="${SYSTEM_DEVELOPER_DIR}"
fi 

if [ "${OTEST}" = "" ]; then
    OTEST="${DEVELOPER_DIR}/Tools/otest"
fi

if [ "${OTEST_TARGET}" = "" ]; then
    OTEST_TARGET="${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}"
fi

RunTargetUnitTestsForArch() {
    echo "${DEVELOPER_DIR}/Tools/RunTargetUnitTests:0: note: Starting tests for ${1}"

    if [ "${DYLD_FRAMEWORK_PATH}" = "" ] ; then
        DYLD_FRAMEWORK_PATH="${BUILT_PRODUCTS_DIR}:${DEVELOPER_DIR}/Library/Frameworks"
    else
        DYLD_FRAMEWORK_PATH="${BUILT_PRODUCTS_DIR}:${DEVELOPER_DIR}/Library/Frameworks:${DYLD_FRAMEWORK_PATH}"
    fi

    export DYLD_FRAMEWORK_PATH

    echo "OTEST=${OTEST}"

    arch -arch "${1}" "${OTEST}" "${OTEST_TARGET}"

    unset DYLD_FRAMEWORK_PATH

    echo "${DEVELOPER_DIR}/Tools/RunTargetUnitTests:0: note: Completed tests for ${1}"
}

SkipTargetUnitTestsForArch() {
    echo "${DEVELOPER_DIR}/Tools/RunTargetUnitTests:0: note: Skipped tests for ${1}"
}

if [ "${TEST_AFTER_BUILD}" = "YES" ]; then
    # Run the unit tests once per requested and supported architecture.
    for TEST_ARCH in ${ARCHS}; do
        case "${NATIVE_ARCH_ACTUAL}" in
        i386)
            if [ "${TEST_ARCH}" = "i386" ]; then
                RunTargetUnitTestsForArch "${TEST_ARCH}"
            else
                SkipTargetUnitTestsForArch "${TEST_ARCH}"
            fi
            ;;

        x86_64)
            if [ "${TEST_ARCH}" = "i386" -o "${TEST_ARCH}" = "x86_64" ]; then
                RunTargetUnitTestsForArch "${TEST_ARCH}"
            else
                SkipTargetUnitTestsForArch "${TEST_ARCH}"
            fi
            ;;

        *)
            RunTargetUnitTestsForArch "${TEST_ARCH}"
            ;;
        esac
    done
fi

I know how it was working with OCTest and I suppose XCTest works in a similar manner. 我知道它是如何与OCTest一起工作的,我认为XCTest以类似的方式工作。

I used to run my tests using WaxSim , which is an utility that accesses private simulator frameworks. 我曾经使用WaxSim运行我的测试, WaxSim是一个访问私有模拟器框架的实用程序。 The use of WaxSim is not really important here, it just enables us to inspect the internals. 在这里使用WaxSim并不重要,它只是让我们检查内部。

The run command was running MyApp.app in the simulator. run命令在模拟器中运行MyApp.app To start the tests, first you have to inject them (they are compiled to a separate bundle!). 要开始测试,首先必须注入它们(它们被编译为单独的包!)。 The command was looking like this 命令看起来像这样

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

Where the INJECTION_FRAMEWORK_PATH was defined like this: 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"

Also note the -SenTest All command line parameters which were telling the the framework which tests to run. 另请注意-SenTest All命令行参数,它们告诉框架要运行哪些测试。

Basically, the application was running as always but there was another bundle injected to run the tests one by one and then exit the whole application. 基本上,应用程序一如既往地运行,但是注入了另一个bundle来逐个运行测试,然后退出整个应用程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM