ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Swift Testingについて
Swiftを使用してコードをテストするための新しいパッケージ、Swift Testingが登場しました。このパッケージの新しいパワフルなAPIの構成要素、一般的なテストワークフローでの応用方法、XCTestおよびオープンソースSwiftとの関連性について解説します。
関連する章
- 0:00 - Introduction
- 0:59 - Agenda
- 1:20 - Building blocks
- 1:58 - Building blocks: @Test functions
- 3:07 - Building blocks: Expectations (#expect and #require)
- 6:02 - Building blocks: Traits
- 6:49 - Building blocks: @Suite types
- 8:34 - Building blocks: Designed for Swift
- 9:14 - Common workflows
- 9:29 - Common workflows: Tests with conditions
- 10:56 - Common workflows: Tests with common characteristics
- 13:13 - Common workflows: Tests with different arguments
- 17:35 - Swift Testing and XCTest
- 21:52 - Open source
- 23:29 - Wrap up
リソース
- Adding tests to your Xcode project
- Forum: Developer Tools & Services
- Improving code assessment by organizing tests into test plans
- Running tests and interpreting results
- Swift Testing
- Swift Testing GitHub repository
- Swift Testing vision document
関連ビデオ
WWDC24
-
ダウンロード
こんにちは Stuart Montgomeryです 本セッションでは Swift Testingをご紹介します 優れたユーザー体験を実現するには 品質と信頼性が重要です 自動テストは 長期にわたり ソフトウェアの品質を高めて 維持するための 実績のある方法です そこで今年は まったく新しいツールセットを導入して Swiftコードのテストを これまで以上に簡単かつ強力にします Swift Testingは Swiftでコードのテストを行うための オープンソースのパッケージです テストの記述と整理を行うための 強力な機能が含まれています 失敗が発生した時には実用的な 詳細情報を提供し 大規模なコードベースにも 洗練された方法で対応します
Swift Testingは Swift向けに設計され 並列処理やマクロなどの 最新機能に対応しています LinuxやWindowsなどのすべての 主要プラットフォームをサポートしています また 開発プロセスがオープンであるため その進化に あらゆるユーザーとコミュニティが 貢献することができます このセッションで まず最初にお話しするのは 知っておくべき基本概念である Swift Testingの構成要素についてです 続いて 一般的なワークフローを いくつかご紹介します テストをカスタマイズする方法や 異なる引数でテストを繰り返す方法などです Swift TestingとXCTestの 関係性についても説明します そして最後にお話しするのが オープンソースコミュニティにおける この新しいプロジェクトの役割についてです ではまず Swift Testingの 構成要素を見ていきましょう これまでにアプリのテストを 記述したことがない場合は 最初にテストバンドルターゲットを プロジェクトに追加します > > の 順に選択します
次に セクションの を検索します
Xcode 16のこのテンプレートでは Swift Testingがテストシステムとして デフォルトで選択されます 新しいターゲットの名前を指定し をクリックするだけです このアプリにはテストターゲットがあるので 最初のテストはそこに記述します まず Testingモジュールを インポートします
次にグローバル関数を記述します
そしてこれに@Test属性を追加します
@Test属性は1つ目の構成要素です これは関数がテストであることを示します この属性を追加すると Xcodeで認識され 実行ボタンが横に表示されます テスト関数は @Test属性を持つ 通常のSwift関数です グローバル関数でも 型内のメソッドでもかまいません asyncやthrowsとしてマークでき 必要に応じて グローバルアクターに分離することもできます 次にテストを作り 実際に検証してみるために 関数の本体にコードを記述しましょう このテストでは ビデオファイルのメタデータが 期待通りであることを確認します まず 確認したいビデオと 期待されるメタデータを 初期化します
これらの型はこのアプリのモジュールで 宣言されているため ここでエラーが出ます 最初にこれをインポートする必要があります
注意点ですが このインポートでは 小文字の@testable属性を使います
これはSwift Testingに固有ではなく 一般的な言語機能ですが アクセスレベルがinternalである このような型を 参照できるようになります 次に#expectマクロを使用して ビデオのメタデータが正しいか確認します
#expectマクロは expectationを実行します これが Swift Testingの2つ目の構成要素です
#expectマクロなどで expectationを実行することで 期待される条件が 満たされているか検証できます これには通常の式と言語演算子を使えます 失敗した場合は ソースコードと部分式の値を キャプチャします 最初のテストを実行して どうなるか見てみましょう
赤いXアイコンで示されているように 失敗したようです テストの失敗メッセージをクリックし を選択すると この行で何が間違っていたのか 詳しく確認できます
この結果ビューには詳細として #expectマクロに渡された式と 部分式の値が 表示されます メタデータを展開すると そのプロパティを比較できます
再生時間と解像度のフィールドが どちらも一致していないようです これを見て思いあたる点があります Videoの型が初期化後に メタデータを 読み込んでいないのではないかと思います これを修正するには 分割エディタで Videoイニシャライザに移動し プロパティが代入されているのを確認します
では テストを再実行しましょう
今度は成功しました 良かったです #expectマクロはとても柔軟です 演算子やメソッド呼び出しを含む 任意の式を渡すことができ 失敗した場合は 詳細な結果が表示されます 例をいくつか紹介します ==演算子を使用でき 失敗した場合は 左辺と右辺がキャプチャされ表示されます .isEmptyなどの プロパティにアクセスできます 配列上の.containsなどのメソッドを 呼び出すこともできます ご覧のように エラー時は 数値配列の内容が自動的に表示されます こうした操作のために 特別なAPIを学ぶ必要はありません #expectマクロを使うだけです
expectationが失敗した場合は 早期にテストを終了したい時もあるでしょう その場合は#requireマクロを使えます 必須のexpectationは 通常のexpectationと似ています ただし tryキーワードがあり 式がfalseの場合はエラーがスローされ テストは失敗となり それ以上先に進みません #requireマクロのもう1つの使い方は オプションの値の安全なアンラップを試行し nilの場合はテストを停止することです
この例では #requireマクロを使用して コレクションの .firstプロパティにアクセスし そのあと 要素のプロパティをチェックしています 「first」プロパティはオプションですが テストの2行目はその値に依存しているため このテストは早期に停止します アンラップされた値がnilの場合は 続行しても意味がないからです 必須のexpectationは このパターンに最適なツールです
テストをプロジェクトにコミットする前に その目的を明確にしましょう そのためには @Test属性でカスタムの表示名を渡します この名前は Test NavigatorやXcodeの ほかの場所に表示されます
表示名は特性の一例ですが この特性が 3つ目の構成要素です
特性には次のような機能があります テストに関する説明情報を追加できます テストの実行タイミングや 実行の有無をカスタマイズできます また テストの動作を変更できます
例をいくつか紹介します 表示名による情報の追加に加え 関連するバグの参照や カスタムタグの追加もできます 特定の条件でのみ テストを実行したい場合は 特性を使ってそれを制御できます 一部の特性は テストの実際の動作に影響を与えます 時間制限を設定したり 1つずつ実行したりできます
最初のテストが作成できたので Videoの型の別の側面を検証する 2つ目のテストを追加しましょう 今度は Xcode 16に組み込まれた テストスニペットを使用して 空のテスト関数をすばやく追加しましょう
このテストはratingという名前にします
そして本体で 先ほどと同様にビデオを作成し #expectで contentRatingが デフォルト値であることを検証します
これら2つのテストをグループ化すると プロジェクト内で見つけやすくなります そこで テストを1つの構造体に ラップします これはVideoTestsという名前にします
これを行うとすぐに 階層がTest Navigatorに反映され クリックして グループで実行することもできます 複数のテストを含むこのような型は テストスイートと呼ばれ これが最後の 4つ目の構成要素です スイートを使って 関連するテスト関数や ほかのスイートをグループ化できます @Suite属性を使用して 明示的に注釈をつけることができますが @Test関数または@Suiteを含む すべての型は 暗黙的にそれ自体が@Suiteと見なされます スイートには インスタンスプロパティを格納できます initまたはdeinitを使用して 各テストの前後にロジックを実行できます 意図しないステートの共有を防ぐために 含まれている@Test関数の インスタンスごとに 個別の@Suiteインスタンスが作成されます
この2つのテストは同じ形で始まります つまり ビデオを作成するための 最初のコード行は同じです これらのテストをスイートにまとめたので 繰り返しを減らせます そこで 目的の行を取り出して このようにストアドプロパティに配置します
これで 2つ目のテストから 目的の行を削除できます
各テスト関数は それを含むスイート型の 新しいインスタンスで呼び出されるため それぞれ独自のビデオインスタンスを取得し 誤ってステートを共有することはありません
では 構成要素をおさらいしましょう テスト関数 expectation 特性 スイートについてお話ししました これらの要素は 次のように Swiftになじむよう設計されています
テスト関数は async/awaitと アクター分離をサポートして Swiftの並列処理と シームレスに連携します
expectationでも async/awaitを使用でき 組み込みの言語演算子をすべて利用できます
expectationと特性では Swiftマクロを利用しているため 失敗時の詳細な結果を確認でき テストごとの情報を コードで直接指定できます
スイートでは 値セマンティクスを備えており 構造体を使用して ステートを分離することが推奨されます
では これらの構成要素を テストでよくある問題に適用し 対処するためのワークフローを 見ていきましょう
テストを実行するタイミングの制御 共通点があるテストの関連づけ 毎回異なる引数での テストの繰り返しについて説明します
まずは条件付きのテストです
テストによっては 特定のデバイスや環境など 実行の条件に制約がある場合があります その場合は .enabled(if: ...)などの 条件特性を適用できます
テストを実行する前に評価する条件を渡し 条件がfalseの場合 テストはスキップとしてマークされます また テストを実行しないように したい場合もあるでしょう この場合は .disabled(...)特性を使用できます テスト関数のコメントアウトといった 他の手法よりも テストの無効化をおすすめします テスト内のコードがまだコンパイルできる ことを確認できるからです
.disabled(...)特性では コメントを受け付けるので テストが無効された理由を説明するのに 使えます コメントは常に 構造化された結果に表示されるため CIシステムの可視性が確保できます
多くの場合 テストを無効にする理由は バグ追跡システムで追跡されている 問題にあります コメントに加えて .bug(...)特性を 他の特性とともに含めて URLで関連する問題を参照できます これにより そのバグ特性を Xcode 16のテストレポートで確認し クリックしてそのURLにアクセスできます
テストの本体全体が 特定バージョンのOSでしか動作しない場合 @available(...)属性を そのテストに配置して どのバージョンで実行するかを制御できます #availableを使用して 実行時にチェックするのではなく @available(...)属性を使います @available(...)属性により テスト用ライブラリで テストにOSバージョンの条件があることが 認識され それを結果に より正確に反映できるようになります
次に 共通の特性を持つテストを 異なるスイートやファイル内のものも含め 関連づける方法について説明します Swift Testingでは カスタムタグを テストに割り当てられます このプロジェクトでは すでにタグを使っています Test Navigatorの下部に タグがすべて表示されます
これらの各タグが適用されている テストを表示するには 新しいモードに 切り替えます
先に作成したテストの1つに タグを適用してみます
これを行うには @Test属性を使用して tags特性をテストに追加します
このテストでは データの書式設定ロジックを検証します このプロジェクトには 書式設定に関連する 別のテストがすでにあるので
このテストにも formattingタグを追加しましょう
これにより Test Navigatorで このタグの下にテストが表示されます
同様にデータの書式設定を検証する テストをもう1つ作成したので ここに追加します
この2つのテストは ビデオ情報の 書式設定に関するものなので サブスイートにグループ化しましょう
これで formattingタグを @Suiteまで移動できます これに含まれるすべてのテストに このタグが継承されます
最後に このタグを 各@Test関数から削除できます タグが継承されたからです
共通点があるテストには タグを関連づけることができます 例えば 特定の機能やサブシステムを検証する すべてのテストに 共通のタグを適用できます これにより 特定のタグを使用して すべてのテストを実行できます また テストレポートで フィルタリングしたり 同じタグを持つ複数のテストが 失敗し始めた時などに 情報を確認することもできます
異なるファイル スイート ターゲットの テストに タグを適用できます 複数のプロジェクトでの タグの共有もできます
Swift Testingでは テストプランに テストを含めるか除外するかを決める時に テストの特定の名前ではなく タグを使用します
最良の結果を得るためには それぞれの状況に最も適したタイプの 特性を必ず使用します すべてのシナリオで タグを使うわけではありません 例えば 実行時の条件を表現する場合は 先ほど説明したように .enabled(if ...)を使用します
Xcodeでの テストタグの使用について詳しくは 「Go further with Swift Testing」を ご覧ください
最後に紹介するワークフローは 特に興味深い内容です 毎回引数を変えて テストを繰り返すものです これがなぜ役立つのか 具体例を見ていきます このプロジェクトには 各ビデオで言及されている 大陸の数を確認するテストが複数あります 各テストは同様のパターンに従います 新しいvideoLibraryを作成し
名前でビデオを検索し #expectマクロを使用して 言及されている大陸の数を確認します
これらのテストは機能しますが 多くの繰り返しが必要で テスト対象のビデオが増えるほど 重複するコードも増えるため 保守が難しくなります また このパターンを使う時は 各テストに一意の関数名を つける必要がありますが これらの名前はわかりにくいので テスト対象のビデオの名前と 一致しなくなる可能性もあります 代わりに パラメータ化テストという機能を使用すれば すべてのテストを 1つのテストとして記述できます この1つ目のテストを パラメータ化テストに変換します
最初のステップとして パラメータをシグネチャに追加します
これを行うとすぐにエラーが生成され このテストに渡す引数を 指定する必要があると表示されます 修正しましょう
とりあえず 3つのビデオの名前だけを入力します
ここでは 引数を読みやすくするために 複数の行に分けていますが 好きなように書式設定できます 最後のステップとして 検索対象となるビデオの名前を テストに渡す引数に置き換えます
これで テスト対象が 複数のビデオになったので 名前を一般化しましょう
テストの完全名にパラメータラベルが 含まれるようになりました
ただし 必要に応じて 表示名やほかの特性を引数の前に渡して 指定することもできます
では テストを実行して どうなるか見てみましょう
できました 成功した結果としてTest Navigatorで それぞれのビデオが個別のテストのように 下に表示されています この構造により とても簡単に 引数の追加やテスト範囲の拡大ができます 残りのビデオをすべて リストに追加しましょう 新しいビデオもいくつかあります
この時点で 古い@Test関数は不要になったので 削除できます
再度テストを実行し 今度も成功するか確認しましょう
最後のほうに追加した 新しいビデオの1つが原因で 失敗しているようです 引数をクリックすると 詳細と失敗したexpectationを 確認できます
この問題を調べるには デバッガを使って再実行すると便利ですが 時間を節約するために 失敗した引数だけを再実行してみましょう Xcode 16では 引数を個別に実行できるようになりました Test Navigatorで 実行ボタンをクリックするだけです ただしその前に ブレークポイントを テストの先頭に追加しましょう
もう一度実行してみます
デバッガに表示されるvideoNameは 「Scotland Coast」なので まさに目的の引数を使用して このテストを実行していることがわかります ここから デバッグをさらに進めて 失敗の原因を特定できます 概念的には パラメータ化テストは 単一のテストを for…inループで 複数回繰り返すのに似ています この例ではvideoNamesの配列があり これをループ処理してテストを実行します ただし for...inループのこうした使い方には 欠点がいくつかあります
パラメータ化テストでは 各引数の詳細を結果で明確に確認できます 引数を個別に再実行できるため きめ細かなデバッグが可能です また 各引数を並列処理して より効率的に実行できるため すばやく結果を得ることができます
パラメータ化テストは ここで説明した方法よりも 高度な使い方ができます 例えば 2組の入力の すべての組み合わせをテストできます 「Go further with Swift Testing」で 詳細をご確認ください
このパターンを使ったテストがある場合は パラメータ化テスト関数に 変換するのが最善です 関数にパラメータを追加し
for...inループを削除して
引数を@Test属性まで移動すれば 完了です
次に Swift TestingとXCTestの 関係性についてお話ししましょう XCTestをすでに利用している場合は この新しいテストシステムとの比較や テストの移行方法について 疑問に思うかもしれません Swift Testingには XCTestと似ている点もありますが 注意すべき重要な違いもいくつかあります すでに説明した構成要素のうちの 3つを比較してみましょう テスト機能 expectation スイートです
XCTestのテストは 名前が「test」で始まるメソッドです 一方 Swift Testingではテストを @Test属性で明示的に指定するため 曖昧さがありません
Swift Testingでは 様々な関数がサポートされており インスタンスメソッドを 型で使えるのはもちろん 静的関数やグローバル関数も 必要に応じて使用できます XCTestとは異なり Swift Testingでは特性を使えるので テストごとまたはスイートごとに 情報を指定できます
また Swift Testingでは 別のアプローチで並列化を行います Swift並列処理を使用して インプロセスで動作し iPhoneやApple Watchなどの 物理デバイスをサポートします
expectationは 2つのシステム間で大きく異なります XCTestでは この概念はアサーションと呼ばれ XCTAssertで始まる多くの関数を使って その機能を表します Swift Testingのアプローチは別であり 2つの基本的なマクロである #expectと#requireのみ使用します
多くの専用の関数を使う代わりに 通常の式と言語演算子を #expectまたは#requireに 渡すことができます
例えば ==を使用して 等価性をチェックしたり 大なり演算子を使って 2つの値を比較できます
また 反対の演算子で expectationを否定するのも簡単です
テストの失敗後にテストを停止する時も 処理方法が異なります XCTestでは continueAfterFailureプロパティに falseを指定し それに続くアサーションが失敗すると テストが停止します
Swift Testingでは #expectの代わりに#requireを使用して 必須のexpectationにすることができ 失敗するとエラーがスローされます これにより どのexpectationで テストを停止するか選択でき テストの進行に合わせて 切り替えることもできます
スイートの型については XCTestがサポートするのはクラスだけで XCTestCaseから継承する必要があります
Swift Testingでは 構造体 アクター クラスを使用できます 推奨の方法である構造体では 値セマンティクスを使用して 意図しないステートの共有による バグを回避できます
スイートは@Suite属性で 明示的に示すこともできますが テスト関数やネストされたスイートを含む すべての型に対して暗黙的に設定されます この属性が必要となるのは 表示名やほかの特性を指定する時だけです
各テストの実行前にロジックを実行する場合 XCTestではsetUpメソッドを使いますが Swift Testingでは 型のイニシャライザを使い asyncまたはthrowsにできます
各テストの後に ロジックを実行する必要がある場合は デイニシャライザを指定できます デイニシャライザを使えるのは スイートの型がアクターかクラスの時だけで それが スイートに構造体ではなく参照型を使う 最も一般的な理由です
最後に Swift Testingでは ネストされた型によって テストをサブグループにグループ化できます
XCTestとSwift Testingのテストは 単一のターゲットで共存できるため 移行する場合は 段階的に進めることができ 新しいターゲットを 最初に作成する必要はありません
同様の構造を持つ 複数のXCTestメソッドを移行する場合は 1つのパラメータ化テストに それらのメソッドを統合できます これは先ほど説明した通りです テストメソッドが1つしかない XCTestクラスについては グローバルの@Test関数に 移行することを検討してください また テストに名前をつける時に 先頭に「test」という語は不要です
次のようなテストでは XCTestを引き続き使用してください XCUIApplicationなどの UI自動化APIや XCTMetricなどの 性能テストAPIを使ったテストです これらは Swift Testingのサポート対象外です
Objective-Cでしか 記述できないテストにも XCTestを使用する必要がありますが Swift Testingを使用すると 別の言語で記述されたコードを 検証するテストを Swiftで作成できます 最後に Swift Testingのテストから XCTestアサーション関数を呼び出すことや 逆に XCTestから#expectマクロを 呼び出すのは避けてください
「Migrating a test from XCTest」の ドキュメントをご覧ください アサーションの変換や 非同期待機状態の処理などについて 様々な詳細情報が記載されています
以上 Swift Testingの機能について説明し その使い方をいくつか紹介しました この新しいパッケージは まだ始まったばかりであり コミュニティで進化し続けていくことを とても楽しみにしています Swift Testingはオープンソースとして GitHubでホストされています 最近発表された Swiftlang Organizationに 近日中に移行します これはSwift並列処理をサポートする AppleのすべてのOSに加えて LinuxやWindowsでも動作します そして注目点として これらすべてのプラットフォームに共通する コードベースを備えています 複数の実装を抱えるXCTestに比べて 大きな改善です そのため プラットフォーム間でテストを移動しても 一貫性のある動作となり 異なるプラットフォームでも 機能の同等性が高くなります
Swift Testingは Swiftエコシステムの主なツールとIDEに 統合されています コマンドラインの Swift Package Manager Xcode 16 最新版のSwift Extensionに対応した VS Codeと連携します
Swift Testingの コマンドラインでの使用を見てみましょう このシンプルなパッケージは Xcode 16の新規パッケージ用の テンプレートを使って作成したものです ターミナルから「swift test」と入力すれば このパッケージのテストを実行できます
XCTestとSwift Testingの 両方のテストが実行されます 成功と失敗の結果が 色分けされた出力で表示され Xcodeの場合と同様に 詳細な失敗メッセージも示されます Swift Testingには オープンな機能提案プロセスがあり その進化について Swift Forumsで意見を交換しています 機能提案の作成や共同提出 ドキュメントの改善 バグレポートの提出などを通じて ぜひご参加ください あらゆる貢献を歓迎いたします 以上 Swift Testingのご紹介でした expectationや パラメータ化テストなどの 強力な機能を使用して コードの品質を高め 特性を使用して テストをカスタマイズし GitHubとフォーラムに参加して このパッケージの未来を形作りましょう 「Go further with Swift Testing」も ぜひチェックして テストを改善するための様々な方法を ご確認ください ご視聴ありがとうございました
-
-
1:54 - Write your first @Test function
import Testing @Test func videoMetadata() { // ... }
-
2:35 - Validate an expected condition using #expect
import Testing @testable import DestinationVideo @Test func videoMetadata() { let video = Video(fileName: "By the Lake.mov") let expectedMetadata = Metadata(duration: .seconds(90)) #expect(video.metadata == expectedMetadata) }
-
4:24 - Fix a bug in the code being tested
// In `Video.init(...)` self.metadata = Metadata(forContentsOfUrl: url)
-
6:06 - Add a display name to a @Test function
@Test("Check video metadata") func videoMetadata() { let video = Video(fileName: "By the Lake.mov") let expectedMetadata = Metadata(duration: .seconds(90)) #expect(video.metadata == expectedMetadata) }
-
6:58 - Add a second @Test function
@Test func rating() async throws { let video = Video(fileName: "By the Lake.mov") #expect(video.contentRating == "G") }
-
7:18 - Organize @Test functions into a suite type
struct VideoTests { @Test("Check video metadata") func videoMetadata() { let video = Video(fileName: "By the Lake.mov") let expectedMetadata = Metadata(duration: .seconds(90)) #expect(video.metadata == expectedMetadata) } @Test func rating() async throws { let video = Video(fileName: "By the Lake.mov") #expect(video.contentRating == "G") } }
-
8:04 - Factor a common value into a stored property in the 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") } }
-
9:32 - Specify a runtime condition trait for a @Test function
@Test(.enabled(if: AppFeatures.isCommentingEnabled)) func videoCommenting() { // ... }
-
9:49 - Unconditionally disable a @Test function
@Test(.disabled("Due to a known crash")) func example() { // ... }
-
10:15 - Include a bug trait with a URL along with other traits
@Test(.disabled("Due to a known crash"), .bug("example.org/bugs/1234", "Program crashes at <symbol>")) func example() { // ... }
-
10:33 - Conditionalize a test based on OS version
@Test @available(macOS 15, *) func usesNewAPIs() { // ... }
-
10:42 - Prefer @available on @Test function instead of #available within the function
// ❌ Avoid checking availability at runtime using #available @Test func hasRuntimeVersionCheck() { guard #available(macOS 15, *) else { return } // ... } // ✅ Prefer @available attribute on test function @Test @available(macOS 15, *) func usesNewAPIs() { // ... }
-
11:22 - Add a tag to a @Test function
@Test(.tags(.formatting)) func rating() async throws { #expect(video.contentRating == "G") }
-
11:48 - Add another data formatting-related test with the same tag
@Test(.tags(.formatting)) func formattedDuration() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "By the Lake")) #expect(video.formattedDuration == "0m 19s") }
-
11:56 - Group related tests into a sub-suite
struct MetadataPresentation { let video = Video(fileName: "By the Lake.mov") @Test(.tags(.formatting)) func rating() async throws { #expect(video.contentRating == "G") } @Test(.tags(.formatting)) func formattedDuration() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "By the Lake")) #expect(video.formattedDuration == "0m 19s") } }
-
12:05 - Move common tags trait to @Suite attribute, so the suite's @Test functions will inherit the tag
@Suite(.tags(.formatting)) struct MetadataPresentation { let video = Video(fileName: "By the Lake.mov") @Test func rating() async throws { #expect(video.contentRating == "G") } @Test func formattedDuration() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "By the Lake")) #expect(video.formattedDuration == "0m 19s") } }
-
13:27 - Example of some repetitive tests which can be consolidated into a parameterized @Test function
struct VideoContinentsTests { @Test func mentionsFor_A_Beach() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "A Beach")) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } @Test func mentionsFor_By_the_Lake() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "By the Lake")) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } @Test func mentionsFor_Camping_in_the_Woods() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "Camping in the Woods")) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } // ...and more, similar test functions }
-
14:07 - Refactor several similar tests into a parameterized @Test function
struct VideoContinentsTests { @Test("Number of mentioned continents", arguments: [ "A Beach", "By the Lake", "Camping in the Woods", "The Rolling Hills", "Ocean Breeze", "Patagonia Lake", "Scotland Coast", "China Paddy Field", ]) 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) } }
-
// Using a for…in loop to repeat a test (not recommended) @Test func mentionedContinentCounts() async throws { let videoNames = [ "A Beach", "By the Lake", "Camping in the Woods", ] let videoLibrary = try await VideoLibrary() for videoName in videoNames { let video = try #require(await videoLibrary.video(named: videoName)) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } }
-
17:15 - Refactor a test using a for…in loop into a parameterized @Test function
@Test(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) }
-
22:47 - A newly-created Swift package with two simple @Test functions
import Testing @testable import MyLibrary @Test func example() throws { #expect("abc" == "abc") } @Test func failingExample() throws { #expect(123 == 456) }
-
22:56 - Running all tests for the package from Terminal
> swift test
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。