SwiftPM and Library Unit Testing

CocoaPods has test specs that provide the specifications for CocoaPods to create an Xcode target for building and running unit tests. How would you do something comparable in SwiftPM?

When you initialize a package from scratch, it includes a sample test:

mkdir Sample
cd Sample
swift package init

See the resulting Package.swift and Tests directory for the relevant code.


Run the all tests in one go like this:

swift test

(See swift test --help for more fine‐grained options.)


If you want work with the package—including tests—in Xcode, generate an Xcode project:

swift package generate-xcodeproj
open *.xcodeproj

(These instructions are for the current stable releases. Available Xcode Betas introduce new kinds of integration with SwiftPM from the Xcode side.)

2 Likes

Both of these approaches seem to run the tests on macOS only, even if I add platforms: [ .iOS(.v9) ], to Package.swift.

Is there a way to test on iOS targets using swift test and/or swift package generate-xcodeproj?

While SPM is now supported for dependency management for iOS, it still does not support defining or creating iOS projects.

1 Like

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