How to Specify a Platform Target When Running Swift Test from the Cli

How can I run XCTest for a swift application from the command line?

I was able to get your XCTestCase compiling with the following command:

swiftc \
-F/Applications/Xcode6-Beta5.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks \
-Xlinker -rpath -Xlinker /Applications/Xcode6-Beta5.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks \
-lswiftCore LeapTest.swift \
-o LeapTests

Then you can execute the tests with xctest:

xcrun xctest LeapTests

And to break down those swiftc command line options:

  1. -F... adds XCTest.framework to the framework search paths, enabling it to be imported from Swift
  2. -Xlinker -rpath ... makes sure the XCTest shared library can be found at load time
  3. Without explicitly specifying -lswiftCore, I found that xctest would crash when it tried to run the test suite

Hope that helps!

Running individual XCTest (UI, Unit) test cases for iOS apps from the command line

It is now possible with Xcode 8 using the -only-testing parameter with xcodebuild:

xcodebuild test -workspace <path>
-scheme <name>
-destination <specifier>
-only-testing:TestBundle/TestSuite/TestCase

enter image description here

Use resources in unit tests with Swift Package Manager

Swift 5.3

See Apple Documentation: "Bundling Resources with a Swift Package"

Swift 5.3 includes Package Manager Resources SE-0271 evolution proposal with "Status: Implemented (Swift 5.3)".

Resources aren't always intended for use by clients of the package; one use of resources might include test fixtures that are only needed by unit tests. Such resources would not be incorporated into clients of the package along with the library code, but would only be used while running the package's tests.

  • Add a new resources parameter in target and testTarget APIs to allow declaring resource files explicitly.

SwiftPM uses file system conventions for determining the set of source files that belongs to each target in a package: specifically, a target's source files are those that are located underneath the designated "target directory" for the target. By default this is a directory that has the same name as the target and is located in "Sources" (for a regular target) or "Tests" (for a test target), but this location can be customized in the package manifest.

// Get path to DefaultSettings.plist file.
let path = Bundle.module.path(forResource: "DefaultSettings", ofType: "plist")

// Load an image that can be in an asset archive in a bundle.
let image = UIImage(named: "MyIcon", in: Bundle.module, compatibleWith: UITraitCollection(userInterfaceStyle: .dark))

// Find a vertex function in a compiled Metal shader library.
let shader = try mtlDevice.makeDefaultLibrary(bundle: Bundle.module).makeFunction(name: "vertexShader")

// Load a texture.
let texture = MTKTextureLoader(device: mtlDevice).newTexture(name: "Grass", scaleFactor: 1.0, bundle: Bundle.module, options: options)

Example

// swift-tools-version:5.3
import PackageDescription

targets: [
.target(
name: "Example",
dependencies: [],
resources: [
// Apply platform-specific rules.
// For example, images might be optimized per specific platform rule.
// If path is a directory, the rule is applied recursively.
// By default, a file will be copied if no rule applies.
// Process file in Sources/Example/Resources/*
.process("Resources"),
]),
.testTarget(
name: "ExampleTests",
dependencies: [Example],
resources: [
// Copy Tests/ExampleTests/Resources directories as-is.
// Use to retain directory structure.
// Will be at top level in bundle.
.copy("Resources"),
]),

Reported Issues & Possible Workarounds

  • Swift 5.3 SPM Resources in tests uses wrong bundle path?
  • Swift Package Manager - Resources in test targets

Xcode

Bundle.module is generated by SwiftPM (see Build/BuildPlan.swift SwiftTargetBuildDescription generateResourceAccessor()) and thus not present in Foundation.Bundle when built by Xcode.

A comparable approach in Xcode would be to:

  1. manually add a Resources reference folder to the Xcode project,
  2. add an Xcode build phase copy to put the Resource into some *.bundle directory,
  3. add a some custom #ifdef XCODE_BUILD compiler directive for the Xcode build to work with the resources.
#if XCODE_BUILD
extension Foundation.Bundle {

/// Returns resource bundle as a `Bundle`.
/// Requires Xcode copy phase to locate files into `ExecutableName.bundle`;
/// or `ExecutableNameTests.bundle` for test resources
static var module: Bundle = {
var thisModuleName = "CLIQuickstartLib"
var url = Bundle.main.bundleURL

for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
url = bundle.bundleURL.deletingLastPathComponent()
thisModuleName = thisModuleName.appending("Tests")
}

url = url.appendingPathComponent("\(thisModuleName).bundle")

guard let bundle = Bundle(url: url) else {
fatalError("Foundation.Bundle.module could not load resource bundle: \(url.path)")
}

return bundle
}()

/// Directory containing resource bundle
static var moduleDir: URL = {
var url = Bundle.main.bundleURL
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
// remove 'ExecutableNameTests.xctest' path component
url = bundle.bundleURL.deletingLastPathComponent()
}
return url
}()

}
#endif

Unable to run Xcode unit tests from the command line due to otest error

The issue is that xcodebuild does not natively support application tests in the simulator. To run applications tests in the simulator from the command line you need an open source utility ios-sim and a tweek to your tests target run script.

1) Install ios-sim https://github.com/phonegap/ios-sim

$ curl -L https://github.com/phonegap/ios-sim/zipball/1.9.0 -o ios-sim-1.9.0.zip
$ unzip ios-sim-1.9.0.zip
$ cd phonegap-ios-sim-538ef1a/
$ sudo rake install prefix=/usr/local/

2) Edit your tests target run script. This script is from Atlassian's fabulous documentation. I love Atlassian makers of JIRA, Stash, Confluence, and my new best friend SourceTree. Full disclosure they acquired SourceTree and it's developer then released it for FREE.

Replace existing:

# Run the unit tests in this test bundle.
"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests"

With this: (Source https://confluence.atlassian.com/display/BAMBOO/Xcode)

if [ "$RUN_UNIT_TEST_WITH_IOS_SIM" = "YES" ]; then
test_bundle_path="$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.$WRAPPER_EXTENSION"
ios-sim launch "$(dirname "$TEST_HOST")" --setenv DYLD_INSERT_LIBRARIES=/../../Library/PrivateFrameworks/IDEBundleInjection.framework/IDEBundleInjection --setenv XCInjectBundle="$test_bundle_path" --setenv XCInjectBundleInto="$TEST_HOST" --args -SenTest All "$test_bundle_path"
echo "Finished running tests with ios-sim"
else
"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests"
fi

enter image description here

3) Run your tests

xcodebuild \
-sdk iphonesimulator6.1 \
-project DC\ Wire\ Sizer.xcodeproj \
-target DC\ Wire\ Sizer\ Tests \
-configuration Debug \
RUN_UNIT_TEST_WITH_IOS_SIM=YES

...

Test Suite 'All tests' finished at 2013-06-06 16:03:35 +0000.
Executed 104 tests, with 0 failures (0 unexpected) in 0.991 (1.063) seconds

Finished running tests with ios-sim
Showing first 200 notices only

** BUILD SUCCEEDED **

Useful Commands:

xcodebuild -showsdks

$ xcodebuild -showsdks
OS X SDKs:
Mac OS X 10.7 -sdk macosx10.7
OS X 10.8 -sdk macosx10.8

iOS SDKs:
iOS 6.1 -sdk iphoneos6.1

iOS Simulator SDKs:
Simulator - iOS 4.3 -sdk iphonesimulator4.3
Simulator - iOS 5.0 -sdk iphonesimulator5.0
Simulator - iOS 5.1 -sdk iphonesimulator5.1
Simulator - iOS 6.0 -sdk iphonesimulator6.0
Simulator - iOS 6.1 -sdk iphonesimulator6.1

xcodebuild -list -project

$ xcodebuild -list -project DC\ Wire\ Sizer.xcodeproj
Information about project "DC Wire Sizer":
Targets:
DC Wire Sizer
DC Wire Sizer Tests
In Work Unit Test

Build Configurations:
Debug
Release_AppStore
Release_TestFlight

If no build configuration is specified and -scheme is not passed then "Release_AppStore" is used.

Schemes:
DC Wire Sizer
DC Wire Sizer - App Store
InWorkUnitTest

Application Test vs Logic Tests
The unit test target that is created for new projects is an Application Unit Test. It injects your test code into the app by setting BUNDLE_LOADER and TEST_HOST in the build settings.
The test targets you create from the New Target menu are logic tests. Logic test are stand alone independent from your application.

This is my Bundle Loader and Test Host Values.

Bundle Loader: $(BUILT_PRODUCTS_DIR)/DC Wire Sizer.app/DC Wire Sizer
Test Host: $(BUNDLE_LOADER)

Potential issue: if you have multiple versions of Xcode installed. Then you need to check your xcode-select setting:

gdunham: ~$ xcode-select -print-path
/Applications/Xcode.app/Contents/Developer

Swift: iOS Deployment Target Command Line Flag

TL;DR? > Go to 3. Solution

1. Preprocessing in Swift

According to Apple documentation on preprocessing directives:

The Swift compiler does not include a preprocessor. Instead, it takes
advantage of compile-time attributes, build configurations, and
language features to accomplish the same functionality. For this
reason, preprocessor directives are not imported in Swift.

That is why you have an error when trying to use __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0 which is a C preprocessing directive. With swift you just can't use #if with operators such as <. All you can do is:

#if [build configuration]

or with conditionals:

#if [build configuration] && ![build configuration]

2. Conditional compiling

Again from the same documentation:

Build configurations include the literal true and false values,
command line flags, and the platform-testing functions listed in the
table below. You can specify command line flags using -D <#flag#>.

  • true and false: Won't help us
  • platform-testing functions: os(iOS) or arch(arm64) > won't help you, searched a bit, can't figure where they are defined. (in compiler itself maybe?)
  • command line flags: Here we go, that's the only option left that you can use...

3. Solution

Feels a bit like a workaround, but does the job:
Other Swift flags

Now for example, you can use #if iOSVersionMinRequired7 instead of __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0, assuming, of course that your target is iOS7.

That basically is the same than changing your iOS deployment target version in your project, just less convenient...
Of course you can to Multiple Build configurations with related schemes depending on your iOS versions targets.

Apple will surely improve this, maybe with some built in function like os()...



Related Topics



Leave a reply



Submit