Swiftの新テストライブラリ「swift-testing」特徴と導入
公開日: 2023年10月
本記事は、2023年10月時点の情報に基づいています。swift-testing
の進化や変更については、公式のドキュメントや関連リソースを参照してください。
はじめに
Swiftの開発環境は日々進化を続けています。その中でもユニットテストの領域において、Appleが最近公開したswift-testing
ライブラリが注目を浴びています。このライブラリに組み込まれているTesting
というフレームワークは、多彩な新機能を持ち合わせており、現在のXCTestから大きく進化しています。現段階ではXcodeとの直接的な統合は見られませんが、将来的には統合される可能性があります。
今回の記事では、この新ライブラリの特徴、XCTestとの違い、そして移行方法について詳しく解説していきます。
背景: なぜswift-testingなのか?
XCTestの歴史
XCTest
は、もともとObjective-C
のテストライブラリとして開発され、後にSwiftにも対応しました。しかし、Swiftとの一部の相性問題や、現代のベストプラクティスとの乖離が指摘されてきました。例として以下が挙げられます。
- テストを発見するためにObjective-Cランタイムに依存していること
- Swiftで利用できない
NSInvocation
のようなAPIに依存していること - テストサブクラスでの暗黙的アンラップ型(
IUO
)プロパティが必要になること -
Swift Concurrency
とシームレスに統合するのが困難であること
新しい方向性
上記の問題に対応するために、swift-testing
という新しいライブラリの提案とリリースがなされました。これはXCTest
の新たな代替となり数年の間に移行される可能性があります。
このライブラリに組み込まれているTesting
というフレームワークには以下の3つの重要なコンポーネントが含まれています
-
#expect()
および#require()
- 条件を検証したり、失敗を投げることができるExpression Macros
-
@Test
および@Suite
- テスト関数とテストスイートの型を宣言するためのAttached Macros
-
Traits
- テストのカスタマイズを支援する機能
Swiftの新しいアプローチは、現代的な設計を取り入れ、Objective-Cランタイムの依存を少なくしています。これにより、Swift Concurrencyともスムーズに組み合わせることができます。さらに、本ライブラリを使用すると、テストの検出や実行を効率的に管理できるだけでなく、パラメータを持ったテストやその他の高度なテストケースもサポートできます。この新しいAPIは、Swiftでのテストの作成や実行をさらに向上させるための強力な基盤となります。
ここからは、上記で述べた重要な3つのコンポーネントについて、詳しく説明します。
#expect()
と#require()
について
XCTAssert
には、XCTAssertEqual()
やXCTAssertLessThan()
など、40以上のテストメソッドがあります。このような特定な複数の期待値を評価するAPIは、JUnit
やRSpec
といったテストライブラリでも見られます。しかし、これらのAPIには以下のような拡張性の問題点があります。
- APIの数が多いため、新規ユーザーの学習曲線が急になり、誤解や不明確なテスト結果のリスクが高まること
- 一部の複雑な使用ケースがサポートされていない可能性があること。
- テストライブラリのメンテナが、複数のユースケースをサポートするために専用APIを追加する必要があり、メンテナンスコストが高くなること
- 関数のオーバーロードが増えることで、適切な関数の選択や理解が難しくなり、コードの複雑さが増してしまうこと
新しい#expect()
および#require()
というExpression Macros
を導入することで、40以上存在していたテストメソッドの数を極端に減らすことができます。また、このExpression Macros
の導入によりテスト期待値の情報量を増やすことができます。例えば、以下の期待値は、失敗時にfalse
のみならず、関連するオペランドや演算子の情報もキャプチャします。
以下の例を見てみましょう。以下の例では、#expect(a.contains(b))
の失敗時の出力情報がXCTAssertTrue
の場合と比較し情報量が増えていることが確認できます。
let array = [1, 2, 3]
let int = 4
XCTAssertTrue(array.contains(int)) // XCTAssertTrue failed
#expect(array.contains(int)) // failed: (array → [1, 2, 3]).contains(int → 4)
XCTestで利用されているテストメソッドとswift-testing
で利用されているテストメソッドの比較は以下の表のようになります。この表はMigrating a test from XCTestから引用しています。
XCTest | swift-testing |
---|---|
XCTAssert(x) , XCTAssertTrue(x)
|
#expect(x) |
XCTAssertFalse(x) |
#expect(!x) |
XCTAssertNil(x) |
#expect(x == nil) |
XCTAssertNotNil(x) |
#expect(x != nil) |
XCTAssertEqual(x, y) |
#expect(x == y) |
XCTAssertNotEqual(x, y) |
#expect(x != y) |
XCTAssertIdentical(x, y) |
#expect(x === y) |
XCTAssertNotIdentical(x, y) |
#expect(x !== y) |
XCTAssertGreaterThan(x, y) |
#expect(x > y) |
XCTAssertGreaterThanOrEqual(x, y) |
#expect(x >= y) |
XCTAssertLessThanOrEqual(x, y) |
#expect(x <= y) |
XCTAssertLessThan(x, y) |
#expect(x < y) |
XCTAssertThrowsError(try f()) |
#expect(throws: any Error.self) { try f() } |
XCTAssertNoThrow(try f()) |
#expect(throws: Never.self) { try f() } |
try XCTUnwrap(x) |
try #require(x) |
XCTFail("…") |
Issue.record("…") |
上記の表から分かるように多くのテストメソッドが#expect()
に置き換えが可能であることがわかります。
エラーの検証およびオプショナルの処理について
テストにおいては、特定の状況下でエラーが発生するかどうか、あるいは発生しないかどうかを確認する必要がある場合があります。さらに、テストを進行する上で成功が保証されているべき期待値が存在するケースも考えられます。
このようなシチュエーションを扱うために、#require()
というMacroを利用できます。具体的には、このMacroにオプショナル値を渡すことで、値が存在する場合はその値を取得し、nil
の場合にはテストを終了させることができます。これまでの方法としてはXCTUnwrap()
を使っていましたが、このMacroはその代わりとして考えることができます。
let someInt: Int? = 10
let someString: String? = nil
let int = try #require(someInt) // ✅
let string = try #require(someString) // ❌ Expectation failed: someString
加えて、swift-testing
にwithKnownIssue()
という関数が用意されています。この関数は、既知の問題点を明示的に示す目的で使用されます。これにより、既知のバグを持つテスト、時々失敗するテスト、未完了のコードに関するテストなどを実行しても、それらのテストが失敗としてマークされないようにすることができます。この機能を有効にするためには、isIntermittent
引数をtrue
に設定する必要があります。(isIntermittent
をfalse
として実行するとテストでエラーが投げられると、テスト失敗としてマークされます)
@Test func error() throws {
withKnownIssue("なぜかランダムにErrorが発生する😢", isIntermittent: true) {
try throwErrorRandom() // ✅ エラーが投げられても、テスト失敗としてはマークされない
#expect(100 == 100)
}
}
@Test
について
swift-testing
を導入することで、テスト関数をグローバル関数または型内のインスタンスまたは静的メソッドとして定義することが可能となります。ただしテスト関数に対して、常に明示的に@Test
を付与する必要があります。
これまではXCTestCase
を継承したクラス内にテストメソッドを定義する必要があり、かつ、テスト関数名はtest
で始める必要がありましたがこの制約がなくなります。
さらにテスト関数にはasync
, throws
, またはmutating
のキーワードを含めることができます。
以下のように@Test
と#expect
を組み合わせることで柔軟なテストを記述することが可能になります。
import Testing
@Test func 挨拶の文字列が正しいこと() { // ✅ グローバル関数として定義可能
let greeting = "Hello Swift"
#expect(greeting == "Hello Swift")
}
final class SomeTests {
@Test func someValueが999であること() async throws { // ✅ asyncとthrowsのキーワードを含めることが可能
let someValue = try await makeSomeValue()
#expect(someValue == 999)
}
@Test static func someStaticTests() { /* ... */ } // ✅ 静的メソッドとして定義可能
}
テスト名のカスタマイズについて
@Test
属性に文字列リテラルを引数として指定することで、IDE
やコマンドラインに表示されるテスト関数の名前をカスタマイズできます。
たとえば、以下の2つのテストがあるとします。
@Test("〇〇の保存処理に関するテスト") func someSaveTest() { /* ... */ }
@Test func anotherSaveTest() { /* ... */ }
この場合、Xcodeでの出力は次のようになります。
◇ Test "〇〇の保存処理に関するテスト" started.
✔ Test "〇〇の保存処理に関するテスト" passed after 0.001 seconds.
◇ Test anotherSaveTest() started.
✔ Test anotherSaveTest() passed after 0.001 seconds.
@Test
属性の実装は以下のようになっており、displayName
の引数に文字列を指定することができます。
@attached(member) @attached(peer) public macro Suite(
_ displayName: _const String? = nil,
_ traits: any SuiteTrait...
) = #externalMacro(module: "TestingMacros", type: "SuiteDeclarationMacro")
テスト関数の外観や動作をさらにカスタマイズするには、tags(_:)
のようなTraits
を使用する必要があります。これについては、Traits
の節で詳しく説明します。
アクターの分離について
XCTest
では、デフォルトで同期的なテストメソッドをメインアクターで実行していました。一方、swift-testing
では、すべてのテスト関数を任意のタスクで実行します。そのため、テスト関数をメインスレッドで実行したい場合、@MainActor
を使用してメインアクターに隔離するか、MainActor.run(resultType:body:)
を利用してコードを実行する必要があります。
final class MainThreadTests {
// ❌ メインスレッドで実行されないのでテストは失敗する
@Test() func isMainThread1() {
#expect(Thread.isMainThread)
}
// ✅ @MainActorを利用しているのでテストは成功する
@Test @MainActor func isMainThread2() {
#expect(Thread.isMainThread)
}
// ✅ @MainActorを利用しているのでテストは成功する
@Test func isMainThread3() {
Task { @MainActor
#expect(Thread.isMainThread)
}
}
// ✅ @MainActorを利用しているのでテストは成功する
@Test func isMainThread4() async {
await MainActor.run { #expect(Thread.isMainThread) }
}
パラメータ化されたテストについて
パラメータ化されたテストは、異なる入力セットでの同一テストの反復実行をサポートします。これは特定の入力セット、例えば列挙のすべてのケース、整数の範囲、または複数のコレクションの組み合わせなど、多くの異なるシナリオで同じテストを実行する際に非常に便利です。
テスト関数が配列や列挙のすべてのケースをループして検証すると、どの入力値で失敗したかを特定するのが難しくなる場合があります。
enum MonsterType: CaseIterable {
case fire
case water
func canSomething() -> Bool { /* ... */ }
func isSomething(int: Int) -> Bool { /* ... */ }
}
@Test func monsterSomeTest() {
for type: MonsterType in [.fire, .water] {
#expect(type.canSomething())
}
}
この問題を解決するために、本ライブラリには@Test
属性を通じてパラメータをテスト関数に渡す機能を提供しています。この方法でテストが失敗すると、どの入力値で問題が発生したかを簡単に特定することができます。
例えば、enum MonsterType
に新しいケースが追加された場合、その新しいケースも自動的にテスト関数で検証されます。また、整数の範囲をテスト関数に渡して、その範囲内のすべての整数でテストを実行することもできます。ただし、非常に大きな範囲を使用すると、テストの実行に長い時間がかかるか、完了しない可能性があるため注意が必要です。
enum MonsterType: CaseIterable { /* ... */ }
@Test(arguments: [MonsterType.fire, .water]) // ✅ 特定のケースを指定しテスト関数に渡すことができる
func someMonsterTest1(_ type: MonsterType) {
#expect(type.canSomething())
}
@Test(arguments: MonsterType.allCases) // ✅ `CaseIterable`に準拠している場合は`allCases`を利用できる
func someMonsterTest2(_ type: MonsterType) {
#expect(type.canSomething())
}
@Test(arguments: 1...99) // ✅ 整数の範囲をテスト関数に渡すことができる
func someIntTest(_ int: Int) {
let fireType: MonsterType = .fire
#expect(fireType.isSomething(int: int))
}
複数のコレクションを同時に使用する場合、デカルト積という考え方に基づいて、すべての可能な組み合わせでテストが実行されます。しかし、zip()
関数を使用すると、2つのシーケンスの要素をペアリングして、一度だけテストを実行することができます。
enum MonsterType: CaseIterable { /* ... */ }
@Test(arguments: MonsterType.allCases, 1...99) // ✅ テストは(2 × 99 = 198)回呼ばれる
func someMonsterTest3(_ type: MonsterType, int: Int) {
#expect(type.isSomething(int: int))
}
@Test(arguments: zip(MonsterType.allCases, 1...99)) // ✅ テストの実行は`(.fire, 1)`と`(.water, 2)`の2回になる
func someMonsterTest4(of _ type: MonsterType, int: Int) {
#expect(type.isSomething(int: int))
}
将来Variadic Genericsのサポートが拡張されることで、パラメータ化されたテストのパフォーマンスはより向上します。
テストの利用制限について
テスト関数を新しいバージョンのOSや特定のSwiftバージョンでのみ実行したい場合、@available
属性をテスト関数の宣言時に使用することで、そのテストの実行を制限することができます。
さらに、@available
属性のmessage
引数を使うことで、テストが実行できない状況時のログメッセージを指定することも可能です。
@available(macOS 11.0, *)
@available(swift, introduced: 5.9, message: "Swift 5.9 が必要")
@Test func someTest() { /* ... */ }
@Suite
について
複数のテスト関数を管理する際、それらをテストスイートにまとめると便利です。struct
, actor
, class
には@Suite
属性を適用できます。この@Suite
属性を適用することで、それらをテストスイートとして認識します。
struct
, actor
, class
内に@Test
関数が含まれている場合、それらは自動的にテストスイートとして扱われるため、明示的に@Suite
属性を付ける必要はありません。ただし、Traits
(下記参照)を指定する際は、@Suite
属性を明示的に記述する必要があります。
struct SomeStructTest { // ✅ @Testが含まれるので暗黙的にテストスイートとして扱われる。
private let someBoolValue: Bool
init() {
someBoolValue = true
}
@Test func boolの値がtrueであること() {
#expect(someBoolValue == true)
}
}
@Suite struct SomeStructTest { // ✅ 明示的に記述することも可能
@Test func someTest() { /* ... */ }
}
テストクラスの変換について
XCTest
では、XCTestCase
を継承するために主にclass
を使用していました。しかし、swift-testing
では、struct
やactor
を用いてテストをまとめることが可能です。
Swiftのコンパイラは並行性の安全性を強化するために、クラスの代わりに構造体やアクターの使用が推奨されます。
struct MyStructTest { // ✅ structを利用することが可能
@Test someTest() { /* ... */ }
}
actor MyActorTest { // ✅ actorを利用することが可能
@Test someTest() { /* ... */ }
}
final class MyClassTest { // ✅ classを利用することが可能
@Test someTest() { /* ... */ }
}
XCTest
でsetUp
を使用していた部分は、init
に変換することができます。
また、tearDown
を使用していた部分はdeinit
に変換することができます。ただし、構造体ではdeinit
は利用できないため、deinit
が必要な場合はactor
またはclass
を使用する必要があります。さらに、class
を使用する場合、そのクラスはfinal
で宣言する必要があります。
struct MyStructTest {
private let someStringValue: String
init() { // ✅ setUpの代替としてinitを利用する
someStringValue = "Hello World"
}
/* ... */
}
final class MyClassTests { // ✅ classを利用する場合はfinalが必須となる
private let someBoolValue: Bool?
init() { /* ... */ }
deinit { // ✅ tearDownの代替としてdeinitを利用する
someBoolValue = nil
}
/* ... */
}
テストのサブグループ化について
あるSuite
属性に準拠している型に対して、ネストとしてさらにSuite
属性に準拠している型を定義することができます。これによりテストをサブグループをとして形成することが可能となります。
struct UserTests { // ✅ 暗黙的にテストスイートとして扱われる
@Test func someUserTest() { /* ... */ }
@Suite(.tags("友達のテスト"))
struct FriendsTests { // ✅ テストスイートをネストさせることが可能
@Test func myFriendsTest() { /* ... */ }
@Test func yourFriendsTest() { /* ... */ }
}
}
これを利用する事により、ネストされたSuite
な型にテストの特徴を指定し、ネスト内に存在するすべてのテストに対してその特徴を継承をさせることができます。上記の例では、FriendsTests
の.tags("友達のテスト")
TraitはmyFriendsTest
およびyourFriendsTest
の両方に対してタグ"友達のテスト"
を追加する効果があります。もちろんFriendsTests
にも追加されます
イニシャライザーの必要性について
テスト関数がインスタンスメソッドとして宣言されている場合、そのテスト関数で使用されるすべてのインスタンスは引数なしのイニシャライザか、デフォルト引数を持つイニシャライザで初期化する必要があります(これが暗黙的なものであるか、明示的なものであるか、同期であるか非同期であるかに関わらないです)。
この条件を満たさない場合、コンパイルエラーが生じます。ただし、テスト関数が静的メソッドとして宣言されている場合、イニシャライザの有無に関わらずコンパイルエラーは発生しません。
struct SomeStructTest { // ✅ 暗黙的なinit()が存在している
private let someBoolValue = true
/* ... */
}
struct SomeStructTest { // ✅ 明示的なinit()が存在している
private let someBoolValue: Bool
init() {
someBoolValue = true
}
/* ... */
}
struct SomeStructTest { // ✅ デフォルト引数を持つinit()が存在している
private let someBoolValue: Bool
init(someBoolValue: Bool = true) {
self.someBoolValue = someBoolValue
}
/* ... */
}
struct SomeStructTest {
private let someBoolValue: Bool
@Test func someBoolValueがtrueであること() { /* ... */ } // ❌ ERROR: init()が存在しない
@Test static func someBoolValueがfalseであること() { /* ... */ } // ✅ staticメソッドなのでinitがなくてもコンパイルエラーは発生しない
}
テストスイートの型の利用制限について
@available
属性は@Test
属性が付与されたテスト関数の実行時の利用可能性を制限するために使用できます。しかし、テストスイートの型には@available
属性を適用することはできません。
この条件を満たさない場合、コンパイルエラーが発生します。
@available(iOS 16.0, *) // ❌ The @Suite attribute cannot be applied to this structure because it has been marked @available(iOS 16.0, *).
@Suite struct SomeTests { /* ... */}
Traits
について
テストのTraitsを指定する仕組みを持つことは重要です。swift-testing
では、この仕組みを実現するために、@Test
と@Suite
がAttached Macros
として実装されています。このAttached Macros
の特徴として、Macroの宣言時に引数としてパラメータを設定できる点が挙げられます。これにより、Traits
を直接引数として渡すことが可能となります。具体的には、Trait
、TestTrait
、SuiteTrait
という3つのプロトコルが利用できるようになっています。
// 基底となるプロトコル
public protocol Trait: Sendable { /* ... */ }
// @Testの引数として追加可能なTraitを表すプロトコル
public protocol TestTrait: Trait { /* ... */ }
// @Suiteの引数として追加可能なTraitを表すプロトコル
public protocol SuiteTrait: Trait { /* ... */ }
@Test
と@Suite
の宣言は以下のようになっており、引数としてそれぞれany TestTrait...
とany SuiteTrait...
を渡すことが可能であることがわかります。
@attached(peer)
public macro Test(
_ displayName: _const String,
_ traits: any TestTrait... // 0個以上の`TestTrait`を引数に取れる
)
@attached(member) @attached(peer)
public macro Suite(
_ traits: any SuiteTrait... // 0個以上の`SuiteTrait`を引数に取れる
)
ここからはTestTrait
、SuiteTrait
として実装されている複数のTrait
の説明をします。
.comment
Traitについて
テストの失敗や問題点は、CI
のインターフェースやログファイルなど、テストのソースコードを直接参照できない場所で表示されることが多いです。したがって、これらの場所でテストに関連するコメントを見ることは、問題の解決を迅速に進めるのに役立ちます。
.comment()
を@Test
属性のTrait
引数として明示的に指定することで、コメントを追加することができます。
@Test(
.comment("これがsomeTestのコメント。テストが失敗するとXcode上やCI上に表示される")
)
func someTest() {
let myValue = 100
#expect(myValue.value == 100)
}
上の例では、コメントをハードコーディングしていますが、enum
などを使用してコメントを定義し、複数のテストからその定義を参照することで、繰り返しの入力を減少させることが可能です。
.comment()
を利用してコメントを追加することは、コードを読むだけでは明らかでない情報を追加する場合やテストの操作や動機に関する役立つ情報を提供する場合に最も役に立ちます。
もし、テストがバグや何かしらの問題に関連している場合は、Comment Trait
の代わりにBug Trait
の使用を検討する必要があります。
.bug
Traitについて
テストは、開発者が書いたコードが期待通りに動作していることを確認する手段として重要です。特定のバグを再現、または修正の検証とテストを関連付けることは非常に有用です。
.bug()
を@Test
属性のTrait
引数として明示的に指定することで、関連するバグにコメントを追加することができます。引数としては、整数値や文字列が指定可能です。
@Test(
.bug(1110), // 整数値として紐付けれる
.bug("〇〇のバグが発生する"), // 文字列として紐付けれる
.bug("https://someURL/bugs/1110") // URLに紐づけたと見なされる
.bug("#143") // CI環境等にホストされている場合はGitHub Issueに紐付けたとみなされる
)
func someTest() { /* ... */ }
さらに、.bug()
では第二引数としてBug.Relationship
を指定することができます。これにより、バグとテストの関係性を明示することが可能です。例として、あるテストが以前は通過していたものの、何らかのバグの影響で失敗するようになった場合、.failingBecauseOfBug
を指定します。
@Test(
.comment("何かしらのコメント"),
.bug("iOS17環境だと〇〇のバグが発生する", relationship: .failingBecauseOfBug)
)
func someTest() { /* ... */ }
Bug.Relationship
には以下のオプションが存在します。
-
.uncoveredBug
- テスト実行が失敗し、バグの存在が明らかとなった場合に使用
-
.reproducesBug
- 既に報告されているバグを再現するテストの場合に使用
-
.verifiesFix
- バグの修正が確認され、再発がないことを示す場合に使用
-
.failingBecauseOfBug
- 以前はテストが成功していたが、関連しないバグのために失敗している場合に使用
-
.unspecified
- 上記のどのケースにも当てはまらない場合や、
.bug()
のデフォルト引数として使用
- 上記のどのケースにも当てはまらない場合や、
.enable
Traitと.disable
Traitについて
swift-testing
ライブラリには、特定の条件下でテストをスキップするか実行するかを制御するための.disable()
と.enable()
という2つのTrait
が提供されています。
特定の条件に基づいてテストの実行をコントロールしたい場合、以下のように利用することができます。
@Test(
.disabled() // ✅ 無条件でスキップされる
)
func someTest1() { /* ... */ }
@Test(
.disabled("コメントを含めることが可能")
)
func someTest2() { /* ... */ }
@Test(
.disabled(
if: magnet.isConnected // ✅ Bool値に基づいてテストがスキップされる
"マグネットがくっ付いていたらテストをスキップする"
)
)
func someTest3() { /* ... */ }
@Test(
.enabled(
if: !magnet.isConnected, // ✅ Bool値に基づいてテストが有効化される
"マグネットがくっ付いていない場合にテストを有効にする"
)
)
func someTest4() { /* ... */ }
さらに、.disable()
と.enable()
を組み合わせて、複数の条件を一つのテストに設定することもできます。ただし、複数の条件が設定されている場合、すべての条件がパスしなければなりません。もし一つでも条件が満たされない場合、そのテストはスキップされ、スキップされた理由として最初に失敗した条件のコメントが記録されます。
@Test(
.enabled(if: someCondition1, "テストがスキップされた場合はこのコメントが記録される"),
.disabled(if: someCondition2, "こちらのコメントは記録されない")
/* ... */
)
func combinedTest() { /* ... */ }
このように、swift-testing
ライブラリを使用することで、開発者はテストの実行条件を自由に制御し、目的に応じてテストを実行またはスキップさせることができます。
その他: 一時的なセットアップ方法について
本テストライブラリはまだSwift Package Manager
に統合されていませんが、Swiftのパッケージで本ライブラリを使用し始めたい開発者のための一時的な仕組みが提供されています。
パッケージのテストターゲットにScaffolding.swift
という新しいSwiftのソースファイルを追加し、以下のコードをそのファイルに追加する必要があります。
import XCTest
import Testing
final class AllTests: XCTestCase {
func testAll() async {
await XCTestScaffold.runAllTests(hostedBy: self)
}
}
まとめ
Swiftの開発環境やテスト手法は進化を続けています。XCTest
の既存の制約と問題点を解決する目的で公開されたswift-testing
ライブラリは、Swiftのテスト環境をより良くする鍵となるでしょう。
本ライブラリを導入することにより、Objective-C
からの依存を減少させるだけでなく、Swift Concurrency
とのシームレスな統合や、より直感的なテストの記述が可能となります。また、新規のMacros
やTraits
の採用により、テストの記述や管理が一段と簡潔で効果的に行うことができるようになると予測されます。
仮に、本ライブラリが将来Xcodeに正式に導入されれば、iOSアプリの開発に関連するテスト手法には大きな変化がもたらされることとなります。このような新しい動向を迅速に取り入れることで、アプリの品質向上に大きく寄与することができると期待しています。
開発環境
- Swift compiler version info:
Apple Swift version 5.9 (swiftlang-5.9.0.128.106 clang-1500.0.40.1)
- Xcode version info:
Xcode 15.0 Build version 15A5229m (beta 8)
- swift-testing revision info:
70f5963165929efa473522f8063e1590f85b3b9c
参考引用文献
- swift-testing
- A New Approach to Testing in Swift
- A New API Direction for Testing in Swift
- Testing
- Migrating a test from XCTest
- Organizing tests
- Parameterized testing
- Getting started
- Enabling and disabling tests
- Adding comments to tests
- Associating bugs with tests
- Defining test functions
- Validating errors and issues in tests
- Validating behavior using expectations
- Adding traits to tests
- Build-Time Constant Values
Discussion
Migrating a test from XCTest がリンク切れになっているので、公式ドキュメントの Record Issues から参照した方がよさそう
内容は同じ