Open1

WWDC24: Swift Testing

hirothingshirothings

Linux, Windowsをサポートしている
オープンソース https://github.com/apple/swift-testing
Xcode16でテストターゲット追加時テストのシステムを選択できるようになっている

Test functions

@Test アトリビュート: Testを宣言する。Xcodeはテストと認識し再生ボタンが表示される
@Test("Check xx") といった感じでテストに名前をつけられる

Expectations

#expect マクロ: マクロ内の式をもとにテストを判定し、結果を出力する。マクロを展開するとプロパティごとの差異を表示できる
テストのための特殊なAPIを学ぶ必要がない。#expectマクロを使うだけ


#require マクロ: テストに必要なプロパティが準備されない場合、テスト実行前に早期にテストを終了できる

let method = try #require(paymentMethods.first)
#expect(method.isDefault) // not executed

Traits

@Testアトリビュートでテストの条件等の属性を指定することが可能
下記の例だと特定の環境でのみ実行するテストを指定している

@Test(.enabled(if: AppFeatures.isCommentingEnabled))
func videoCommenting() {
    // ...
}

Suite

テストをグループした1つの単位
structで@Testを囲むことでグルーピングできる
Suite内の@Testはそれぞれ別のインスタンスとして呼ばれるため、Suite内のメンバ変数を誤って共有することはない

struct VideoTests {
    let video = Video(fileName: "By the Lake.mov")

    @Test("Check video metadata") func videoMetadata() {
        let expectedMetadata = Metadata(duration: .seconds(90))
        #expect(video.metadata == expectedMetadata)
    }

    @Test func rating() async throws {
        #expect(video.contentRating == "G")
    }
}

Parameterize Testing
パラメータを渡してパラメータごとのテストを1つのテストで実行できる
パラメータごとにテストを書く必要がなくなった
サイドメニューに失敗したテストのパラメータが表示され、そのパラメータだけ実行できる

struct VideoContinentsTests {
    @Test("Number of mentioned continents", arguments: [
        "A Beach",
        "By the Lake",
        "Camping in the Woods",
    ])
    func mentionedContinentCounts(videoName: String) async throws {
        let videoLibrary = try await VideoLibrary()
        let video = try #require(await videoLibrary.video(named: videoName))
        #expect(!video.mentionedContinents.isEmpty)
        #expect(video.mentionedContinents.count <= 3)
    }
}

コマンドライン, VS Codeでも使える

参考URL

Swift Testingについて
https://www.youtube.com/watch?v=WFnkNcvLnCI

Migrating a test from XCTest
https://developer.apple.com/documentation/testing/migratingfromxctest

XCTestからSwift Testingへの自動変換ツール
https://x.com/giginet/status/1805914170766114852?s=46&t=tkTBnr8gYdwqlBbKD7-bLA