xcodecmakexcode-ui-testing

How to create an Xcode UI Test Target in CMake


Xcode has two template types of testing: Unit Test Targets and UI Test Targets, which run the host application differently. Looking online, I don't see any clear distinction of how these host targets are run differently, just that the unit tests will import a testable bundle to invoke specific classes, while the UI test bundle will create an instance of XCUIApplication at runtime.

Picture from Xcode target creation flow:

ui test bundle vs unit test bundle

If I could find somewhere describing the differences between these targets more technically, then I could start looking at how to change my cmake script to setup each target to match.

CMake has xctest_add_bundle as part of FindXCTest, which seems to create an Xcode Unit Test Target.

There doesn't appear to be a specific way to create a UI Test Target directly with CMake, but how can I alter a xctest_add_bundle test bundle in my CMake script such that I can create a UI Test Target that matches Xcode "UI Testing Bundle"?


Solution

  • There are two key things here.

    First: It's not very well documented, but CMake has a target property called XCODE_PRODUCT_TYPE (see here), but it doesn't say any of the typical values. If you manually open up a project file project.pbxproj in another IDE (I couldn't find this exposed anywhere in the Xcode GUI), you can find that a UITests target will have productType = com.apple.product-type.bundle.ui-testing. Setting this is the key part for making the target seen by Xcode as a UI Tests bundle instead of a unit tests bundle.

    Second: Another SO post about unit testing pointed me to a retired Apple document which discusses "Application unit tests" instead of "UI tests". So old Xcode refers to both of these types as "unit tests".

    CMake's xctest_add_bundle takes the test target name and testee target name. This automatically adds the "Test Host" and "Bundle Loader" build settings mentioned in those apple docs. So xctest_add_bundle seems to be actually creating some sort of hybrid of a "UI Testing Bundle" and a unit test bundle.

    Pulling that all together, in order to make a proper "UI Testing Bundle" as Xcode would make it, you can do:

    # MyApp is your defined app
    find_package(XCTest REQUIRED)
    xctest_add_bundle(UITests MyApp
        Tests/Tests.swift
    )
    set_target_properties(UITests PROPERTIES
        XCODE_ATTRIBUTE_TEST_HOST ""
        XCODE_ATTRIBUTE_BUNDLE_LOADER ""
        XCODE_ATTRIBUTE_TEST_TARGET_NAME MyApp
        XCODE_PRODUCT_TYPE com.apple.product-type.bundle.ui-testing
    )
    

    This still gives the UI test target icon in the Xcode targets list and now properly allows the XCUIApplication to run.