Running tests in Swift package with GitHub actions
Some time ago I published a tiny Swift package IndexedDataStore which tackles a problem of storing data blobs on disk. It could the image data or anything else. When working with Swift packages then it is extremely easy to build and run tests on macOS but when we want to build and run tests on iOS simulator then we need to drop using
swift build and
swift test commands. Fortunately xcodebuild can help here and we can build and run tests without generating a project file ourselves with
swift package generate-xcodeproj. Something I would like to mention about the generate-xcodeproj command is that it generates a project which does not contain resource files. The aim of this blog post is to configure a GitHub action which runs unit-tests both on macOS and iOS.
GitHub actions need to be in the repostory’s
.github/workflows folder. GitHub workflows are defined in YAML files which need to be in that folder. Let’s jump into it and start creating a worflow which runs tests on macOS and iOS. GitHub workflows must define a name, in our case,
name: CI. Secondly we’ll need to define when the workflow should be triggered. Running the workflow whenever pull request is created of when something was pushed to main branch will suffice.
name: CI on: push: branches: [ main ] pull_request: branches: - '*'
The next step is creating a job and defining a number of steps. Jobs require a name, then which resource it needs to use. The first step is the repository checkout followed by running swiftlint which is already part of the enviornment. The linting step uses the default configuration file
.swiftlint.yml at the repository root. At this point we are ready to build and run tests.
jobs: unit_tests: runs-on: macos-latest steps: - name: Repository checkout uses: actions/checkout@v2 - name: Lint run: swiftlint
The simplest way to build and test Swift packages is to use
swift build and
swift test commands which builds and runs tests on macOS. Unfortunately there is not a way to use those commands and setting the deplyoment to iOS. Therefore we’ll need to use
xcodebuild command instead. It will know how to deal with Swift packages and therefore we only need to define scheme and destination arguments. It also makes sense to separate building (
build-for-testing) from running tests (
test-without-building) as it makes it easier to see where a failure happened. We can make the xcodebuild output a little bit cleaner by using xcpretty. In addition it is also reasonable to use
set -o pipefail which makes the pipeline to use an occured error code as the final code of the whole pipeline. Finally, we’ll disable any buffering by setting NSUnbufferedIO to YES.
jobs: unit_tests: runs-on: macos-latest steps: - name: Repository checkout uses: actions/checkout@v2 - name: Lint run: swiftlint - name: Build for macOS run: swift build -v - name: Run macOS tests run: swift test -v - name: Build for iOS run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild build-for-testing -scheme IndexedDataStore -destination "platform=iOS Simulator,OS=latest,name=iPhone 12" | xcpretty - name: Run iOS tests run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild test-without-building -scheme IndexedDataStore -destination "platform=iOS Simulator,OS=latest,name=iPhone 12" | xcpretty
This completes our workflow and we can check in the YAML file which gets then picked up by GitHub. All the actions can be see under the Actions section on GitHub.
We looked into how to add a GitHub workflow for running tests both on macOS and iOS. Although
swift build does not support building the package for iOS we could still do that with xcodebuild.