SwiftPM and Library Unit Testing

Yes. See here:

Those instructions are for Xcode 10. I think Xcode 11 can infer more of the arguments on its own now, but I cannot remember which off the top of my head.

1 Like

It has indirectly supported frameworks for iOS for a long time through generate-xcodeproj. The iOS‐related portions of the generated Xcode project have been in working condition since at least Swift 4.0.

Two important things it cannot do are (a) directly cross‐compile without outside help such as Xcode and (b) specify resources, or by extension application bundles. But these are nothing specific to iOS.

Technically what it doesn’t support yet is declaring a project to be limited to any specific platform. You cannot make an iOS project, because what you end up with is a cross‐platform macOS‐Linux‐iOS‐watchOS‐tvOS‐Android‐Windows project, though it may contain code that doesn’t actually compile successfully for all of them.

2 Likes

That's a fair correction to my over simplification!

After checking back in with Xcode 12 and Swift Package Manager 5.3, it seems like there is still not Swift Package Manager support for running iOS unit tests. I'm only able to run my tests for macOS with swift test.

Is there a way to run unit tests specified by a Package.swift on iOS like CocoaPods test specs?

If not, what is the recommended way to develop and run library unit tests when developing with Swift Package Manager?

You can use Xcode and xcodebuild to interact with packages as well, so e.g. xcodebuild test to run a package's unit tests. To target iOS, simply pass the appropriate -destination to xcodebuild.

Hello Boris,

Does it still require generating the Xcode project and then running xcodebuild? Hopefully we can move beyond that as it makes it a pain to generate and get code coverage of the right items as you lose control of the generated Xcode project unless you commit it to source control and make updating it manually a developer step.

Is that to say that there will not be a swift test iOS destination for the foreseeable future? Is it perceived as not needed? Why?

Sorry, you clarified that Xcode projects are not needed in another thread.

Here's what worked for me for a directory that contained a Package.swift:

xcodebuild -list
xcodebuild -scheme {scheme-from-list} test -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 11'

1 Like

It seems that the testTarget schemes only show up in xcodebuild -list after manually being added in the Xcode UI from Manage Schemes.

Is there a way to get testTargets to show up in xcodebuild -list and be available for xcodebuild without an Xcode UI intervention?

1 Like

Tests are associated with their products in the schemes, similar to how they are for regular Xcode targets. You should be able to get the same behaviour as swift test by using the one scheme that's generated for packages with a single product or the one with -Package suffix if there are multiple products. Just pass that to xcodebuild test and all tests should be running.

(Pre-context: I'm working with @paulb777 on the same thing :smile:)

Thanks for the info, I was able to get xcodebuild -scheme {Name}-Package ... to run tests properly. That's a good fallback for our use case but we're going to have many products and many different test cases (Firebase), so testing the entire suite on a small change to one target/product will take a while and take a significant amount of machine time, so ideally we can get individual .testTargets running.

I'm not sure what you mean by:

I understand the regular Xcode bit, but wasn't sure how to associate a .testTarget with an associated .target or .product. I looked through the API but didn't find a way to specify... is there a naming scheme to follow in order to get them to associate with one another? I tried <TargetName>Tests and xcodebuild -scheme <TargetName> test ... but that didn't work for me.

Thanks!

(Full context, we're working on getting SwiftPM Firebase tests working on GitHub Actions: SPM: Core ObjC unit tests by paulb777 · Pull Request #5959 · firebase/firebase-ios-sdk · GitHub)

IIRC, the test target's dependencies are used to do the association. So I think if <TargetName>Test depends on <TargetName>, the corresponding scheme should run those tests. Let me know if that doesn't work and I'll take a look at the implementation to refresh my memory on how it actually works :)

That doesn't seem to be the case for https://github.com/firebase/firebase-ios-sdk/blob/master/Package.swift. The tests are only available with the package scheme.

I tried renaming the test targets to FirebaseCoreTest and FirebaseStorageTest, but that didn't make a difference

Looks like the way it actually works is this:

So one suggestion to get the behaviour you want with current Xcode would be to add a dummy executable target that gets the right tests based on the SwiftPM logic. Please also file a radar with the behaviour you would like to see.

1 Like

I found that I can create the Unit Test schemes in the Xcode UI, commit the generated shared schemes, and then the unit tests are available to select for CI testing.

I successfully managed to successfully run Swift package unit tests (no xcodeproj file) on an iOS simulator with the following command:
xcodebuild -scheme <scheme> test -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 11'

Find your scheme name by executing:
xcodebuild -list

7 Likes

One issue with the committed generated schemes for the unit tests is that they propagate as clutter to library clients. See Firebase Flooding Project with Build Schemes · Issue #8167 · firebase/firebase-ios-sdk · GitHub.

Is there anyway to keep package schemes from showing up in client apps? Or any progress on the original issue of automated scheme generation support for library products?

2 Likes

Like we have swift build --target TargetName, do we have command to test specific target like swift test --target TargetName?

Yes we do:

swift test --filter TargetName

Run swift test --help for more information.

I am updating an internal SwiftPM package we've had for a few years. The package in the past was testable on macOS and the iOS simulator.

I am now adding an executable product and executableTarget that is macOS only. Is there a way to mark those product/target to only build on macOS?

More Details With Code Really the problem is that adding an `executableTarget` with `resources` leads to Xcode attempting to run `codesign`. Which then fails the build.
...
CodeSign /Users/lovelettr/Desktop/example-2/derivedData/Build/Products/Debug-iphonesimulator/example-1_example-2.bundle (in target 'example-1_example-2' from project 'example-1')
    cd /Users/lovelettr/Desktop/example-2

    Signing Identity:     "-"

    /usr/bin/codesign --force --sign - --timestamp\=none --generate-entitlement-der /Users/lovelettr/Desktop/example-2/derivedData/Build/Products/Debug-iphonesimulator/example-1_example-2.bundle
/Users/lovelettr/Desktop/example-2/derivedData/Build/Products/Debug-iphonesimulator/example-1_example-2.bundle: bundle format unrecognized, invalid, or unsuitable
Command CodeSign failed with a nonzero exit code

If you want to replicate the error locally go to: RLovelett/swiftpm-and-library-unit-testing: This is an example SwiftPM Package used to illustrate an error discussed on the Swift Forums (github.com) and run build.sh.