I know that I can start my app's unit tests with xcodebuild
, but I am wondering what tells the application to run the tests during the launch, is it a special argument sent to the application or it compiled differently in order to run the tests (with XCTest)?
a) System Testing: This type of iOS testing is performed on the system to check if the various components of the system work together. In this testing process, the iOS application is launched on a real Apple device followed by its interaction with the user interface to trigger a specific set or sets of user action(s).
Unit tests have two main purposes: Reduce the number of bugs by making sure that our class/module/component is working as expected with all possible inputs. Reduce the number of regressions by making sure that our new functionality, bug fixes or refactoring hasn't broken any existing functionality.
Unit testing is a software development process in which the smallest testable parts of an application, called units, are individually and independently scrutinized for proper operation. This testing methodology is done during the development process by the software developers and sometimes QA staff.
Xcode uses xctest
and RunTargetUnitTests
script (from /Developer/Tools/RunTargetUnitTests
) for unit testing.
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. 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.
Also info that will be very helpful:
RunUnitTests
accepts ENVIRONMENT variables
, here are some interesting of them
TEST_HOST
- The full path to an executable into which to "inject" the
specified unit test bundle. 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. 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. Do not set this if you are using one of the default
test rigs.
alsoBUNDLE_LOADER
is used as a linker option, which indicates linker to link Testing dynamic library
into your specified binary.
The test bundle target templates have a shell script build phase at the very end that invokes /Developer/Tools/RunUnitTests
. 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.
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. 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. The Test Host setting tells RunUnitTests to launch the specified application and inject your test bundle into it in order to run its tests.
For more information see man
pages of 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
Here is the RunTargetUnitTests
script
#!/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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With