Tl;Dr
Swift 5.1 から追加された--enable-test-discovery
オプションを渡せばよい。
$ swift test --enable-test-discovery
ただし、正しく検出できないバグも残っているらしいので、心配ならば Swift 4.1 から追加された--generate-linuxmain
を利用してテスト実行用のソースを自動生成できる。
$ swift test --generate-linuxmain # 自動生成
$ swift test # 実行
つまり、Swift 4.1 以降であればテスト一覧を自分で列挙する必要はない。
Linux環境で実行すべきテストを列挙する(Swift 5.1未満)
Swift 5.1 においてswift package init
でプロジェクトを初期化すると、Tests
ディレクトリ配下が以下のように生成される(生成時のルートディレクトリはSwiftPackageExample
)。
$ tree Tests
Tests
├── LinuxMain.swift
└── SwiftPackageExampleTests
├── SwiftPackageExampleTests.swift
└── XCTestManifests.swift
このうちLinuxMain.swift
とXCTestManifests.swift
は、Linux などの macOS 以外の OS でテストを実行するためのファイルであり、前者が『エントリポイント』、後者が『実行すべきテストの一覧を列挙』するためのものになっている。
これらが必要なのはmacOS以外の環境では実行すべきテストの一覧を自動的に検出できなかったことにある。
それぞれのソースを簡単に見てみる。
LinuxMain.swift
LinuxMain.swift
では、後者のXCTestManifests.swift
に定義されたメソッドを呼び出すことで、実行すべきテスト一覧を取得し、XCTMain()
関数でテストを実行している。
import XCTest
import SwiftPackageExampleTests
var tests = [XCTestCaseEntry]()
tests += SwiftPackageExampleTests.__allTests() // XCTestManifests.swiftに定義されたメソッドを呼び出し
XCTMain(tests)
XCTestManifests.swift
XCTestManifests.swift
では、①extensionを利用して各テストクラスの『テスト名』と『テストメソッドの参照』のタプルの一覧を返すように定義され、②さらに前述のLinuxMain.swift
から呼び出される__allTests()
関数においてtestCase()
関数でXCTestCaseEntry
に変換して実行すべきテストの一覧を返すようになっている。
#if !canImport(ObjectiveC)
import XCTest
// ①
extension SwiftPackageExampleTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__SwiftPackageExampleTests = [
("testExample", testExample),
]
}
// ②
public func __allTests() -> [XCTestCaseEntry] {
return [
testCase(SwiftPackageExampleTests.__allTests__SwiftPackageExampleTests),
]
}
#endif
--generate-linuxmain
で自動生成する(Swift 4.1+)
Swift 4.1未満ではXCTestManifests.swift
を自分でメンテする必要があり、テストメソッドを新たに追加するごとにテスト一覧の定義を更新する必要があった。
それが Swift 4.1 において--generate-linuxmain
というオプションが追加され、完全に自動生成することが可能になった。
$ swift test --help
...
--generate-linuxmain Generate LinuxMain.swift entries for the package
以下のコマンドでLinuxMain.swift
とXCTManifests.swift
が自動生成される。
$ swift test --generate-linuxmain
前述のXCTestManifests.swift
のコメントをみるとDO NOT MODIFY: This is autogenerated
と記述されており、自動生成されていることがコメントからも読み取れる。
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__SwiftPackageExampleTests = [
--enable-test-discovery
で自動検出する(Swift 5.1+)
Swift 5.1 では--enable-test-discovery
という macOS 以外において、テストの一覧を自動的に検出してテストを実行するオプションが追加された。
$ swift test --help
...
--enable-test-discovery
Enable test discovery on platforms without Objective-C runtime
これを利用すれば前述したLinuxMain.swift
とXCTestManifests.swift
は不要となり、Linux環境でも以下のコマンドで実行できるようになる。
$ swift test --enable-test-discovery
しかし、このオプションにはまだバグが残っているらしい(執筆時点)。
自身のプロダクトで正しく検出できるのかは、--list-tests
オプションでテスト一覧を列挙し、それらの件数が一致するかを確認するとよい。
# on mac
$ swift test --list-tests
SwiftPackageExampleTests.SwiftPackageExampleTests/testExample
# on Linux
$ swift test --enable-test-discovery --list-tests
SwiftPackageExampleTests.SwiftPackageExampleTests/testExample
もっとも、これはこの時点において正しく検出できるかの確認に過ぎないので、自身のプロダクトにおいてLinux環境でのテストが重要なものであれば、このオプションがデフォルトになる(つまり指定が不要になる)まで見送ったほうがよいかもしれない。
Dockerで実行できるようにする
mac のローカル環境でもDockerで実行できるようにしておくと何かと便利である。
以下はSwift 5.1のコンテナで実行するMakefile
の例である。
linux-test:
docker run --rm \
--volume "$(CURDIR):/src" \
--workdir "/src" \
swift:5.1 \
swift test --enable-test-discovery
これは次のように実行できる。
$ make linux-test
ついでにコンテナにログインするターゲットを用意しておいてもよいかもしれない。
linux:
docker run --rm -it \
--volume "$(CURDIR):/src" \
--workdir "/src" \
swift:5.1
--volume
でマウントしているので、ソースの変更などはすべてmac側で行える。
$ make linux
docker run --rm -it \
--volume "/Users/hosonumayuusuke/go/src/github.com/YusukeHosonuma/SwiftPrettyPrint:/src" \
--workdir "/src" \
swift:5.1
root@ff97680b040b:/src#
GitHub Actionsで実行できるようにする
以下のようにruns-on
にubuntu-latest
、container
にswift:5.1
を指定してジョブを定義すればよい。
name: Test
on:
push:
branches:
- master
pull_request:
jobs:
unit-test-in-linux:
runs-on: ubuntu-latest
container: swift:5.1
steps:
- uses: actions/checkout@v2
- name: Run Unit Test
run: swift test --enable-test-discovery
参考PR
自身のOSSプロダクトについて、以下のPRで対応したので参考までに。
https://github.com/YusukeHosonuma/SwiftParamTest/pull/34
LinuxMain.swiftを残してしまっているが、これは本来であれば不要なのでご留意を。
参考記事
終わりに
Swift Package Manager は Swift と共に進化してきたこともあり、古いバージョンについて言及された記事も多数残っているため、参考情報として利用する際には注意が必要なように思う。
かくいう私も Swift 4.1 の時点ですでに追加されていた自動生成用の--generate-linuxmain
の存在を知らず、 30個以上もあるテストの定義を手書きしてしまうところであった (というより、この記事に書いたLinux環境でのテストについて知ったのも最近のことだ)。
今であれば公式のパーサである swift-syntax もあるので、自動生成用のツールを書いてしまうかと思い、ふとすでに存在しないかとググってみたら参考記事に貼ったような情報を得られたという次第であった。
日本語圏においては Swift Package Manager の情報は英語圏に比べてさらに少なく、私自身も『LinuxMain.swiftとは』でググっても殆ど出てこない状況に遭遇したことを思い出し、筆を執ることにした。