Swift Testingは公式から出たSwift用テストフレームワークです。
XCTest
と混在できるため、今日からSwift Testingでテストを書くことができます。
簡単なXCTestからSwift Testingへの書き換え
extension Date {
var christmas: String {
"12/25"
}
}
import XCTest
final class DateTests: XCTestCase {
func testChristmas() {
XCTAssertEqual(Date.christmas, "12/25")
}
}
import Testing
struct DateTests {
@Test("christmas extensionのテスト")
func christmas() {
#expect(Date.christmas == "12/25")
}
}
構成要素
@Test
テストを宣言するマクロ。関数の前に追加する。
XCTestではXCTestCase
を継承し、関数名の頭にtest
をつける。
@Suite
複数のテストをまとめるマクロ。
テスト関数を含むクラスが自動的にテストスイート(複数のテストをまとめたもの)になる。
struct DateTests { // 自動的にテストスイートになる。
@Test func christmas() {
//...
}
}
表示名やtraitを付与したい場合は@Suiteマクロを使用する。
@Suite(.timeLimit(.minutes(60))
struct DateTests {
@Test func christmas() {
//...
}
}
期待値の確認
#expect(_:_:sourceLocation:)
または#require(_:_:sourceLocation:)
を使用し評価します。
final class EventTests: XCTestCase {
func finalYearlyEvents() throws {
XCTAssertNotNil(events.last)
XCTAssertEqual(events.last, .christmas)
}
}
struct EventTests {
@Test func finalYearlyEvents() throws {
try #require(events.last != nil)
#expect(events.last == .christmas)
}
}
Traits
テストやテストスイートに特性を付与することで、テストに注釈を付けたり動作をカスタムすることができる。
それぞれの使い方は公式ドキュメントに詳しく書かれているので、概要とリンクのみとします。
-
enabled/disabled
テストを条件付きで有効または無効にする。 -
timeLimit
テストの制限時間を設定する。
複数の制限時間が設定されている場合、一番短いものが適応される。
テストスイートに制限時間を宣言すると、そのスイート内全てのテストスイートと関数に適応される。 -
serialized
並列実行/順次実行を制御します。 -
bug
バグとテストを関連つけます。 -
tags
テストやテストスイートにタグをつけることができる。 -
コメント(//,/**/)
コードにコメントを追加しテスト結果に情報を提供する。
新しい機能
パラメイトライズ
Swift Testing特有の機能です。異なるパラメータを外部から注入することで、1つのテスト関数から複数のテストを生成することができます。
enum Event {
case valentine, halloween, christmas
var day: String { ... }
}
XCTestでこのようなテストがあるとします。
final class EventTests: XCTestCase {
func testDay() {
XCTAssertEqual(Event.valentine, "2/14")
XCTAssertEqual(Event.halloween, "10/31")
XCTAssertEqual(Event.christmas, "12/25")
}
}
この場合仮にテストが失敗したら再実行はtestDay
単位でしかできず、3つの判定が再度走ることとなります。
一方でこのテストをSwift Testingのパラメイトライズを活用するとこのように書き換えられます。
struct EventTests {
@Test(arguments: [
(Event.valentine, "2/14"),
(Event.halloween, "10/31"),
(Event.christmas, "12/25")
])
func testDay(event: Event, expected: String) {
#expect(event.day == expected)
}
}
テストの実行はtestDay
が各パラメータで3回実行される形になります。どのパラメータで失敗したか一目でわかり、失敗したパラメータのみ再実行できます。
引数はarguments
で指定した順にテスト関数で参照できます。
並列実行と順次実行
テストスイートやテスト関数は指定しない場合、自動で並列実行されます。
順次実行したい場合はserialized
トレイトを付与します。
※ テスト関数に付与する場合は、テスト関数がパラメイトライズを使用していなければ効果はありません。
struct EventTests {
@Test(.serialized, arguments: [
(Event.valentine, "2/14"),
(Event.halloween, "10/31"),
(Event.christmas, "12/25")
])
func testDay(event: Event, expected: String) {
#expect(event.day == expected)
}
}
これにより、3ケース並列してテストされず、前のテストを待ってから次のテストが開始されます。
エラーのテスト
withKnownIssue
を使用すると、既知の問題のみが発生した場合テストは失敗ではなく成功と判定されます。
※既知のエラーに関しては#expect(throws:)
ではなくwithKnownIssue
を使用することが推奨されます。
struct EventTests {
@Test func finalYearlyEvents() throws {
try #require(events.last != nil)
#expect(events.last == .christmas) // これが既知のエラーの場合
}
}
struct EventTests {
@Test func finalYearlyEvents() throws {
try #require(events.last != nil)
withKnownIssue("最後のイベントは年越しです。") {
#expect(events.last == .christmas)
}
}
}
参考