XCTestは、iOS、macOS、watchOS、tvOS向けアプリケーションの単体テストおよび統合テストのためのAppleのフレームワークです。XCTestはXcodeの一部であり、SwiftおよびObjective-Cでのテスト作成をサポートしています。サードパーティのフレームワーク(Quick、Nimble)とは異なり、XCTestはAppleの公式ソリューションであり、Xcode ServerおよびCI/CDと完全に統合されています。Apple Developer(2024)によると、XCTestはApp Storeのトップ100にあるiOSアプリの94%で使用されています。XCTestは、外部依存関係なしで単体テストとUIテストを作成するための安定した基盤を提供します。
重要ポイント
XCTestは、Appleが開発した単体テスト、統合テスト、UIテストのためのフレームワークで、バージョン5.0(2013年)以降Xcodeに組み込まれています。XCTestはOCUnit(SenTestingKit)を置き換え、非同期テスト、パフォーマンステスト、Xcode Server統合をサポートする最新のSwift APIを提供しました。Swift.org(2024)によると、XCTestはSwift Package Managerを含むすべてのAppleプロジェクトにおけるテストの基盤であり、Swift Package Managerは自己検証にXCTestを使用しています。
XCTestはXcode Test NavigatorおよびReport Navigatorと連携し、テストツリー、実行履歴を表示し、ビルド間の結果を比較します。Test Navigatorを使用すると、コードを変更せずに単一のテスト、テストグループ、またはスイート全体を実行できます。結果は緑色(合格)、赤色(失敗)、黄色(スキップ)のアイコンで表示されます。Apple WWDC(2024)によると、Xcode 16は複数のシミュレーターを使用することで並列テスト実行を40%改善しました。
XCTestがサポートするプラットフォーム:iOS 8.0以上、macOS 10.10以上、watchOS 2.0以上、tvOS 9.0以上。各プラットフォームに同じAPIが用意されており、クロスプラットフォームのテスト作成が可能です。Swift Testing — Appleの新しいフレームワーク(2024年発表)— は将来XCTestを補完しますが、完全に置き換えるものではありません。XCTestはAppleエコシステムにおける主要なテストフレームワークであり続けます。
XCTestCaseは、XCTestのすべてのテストクラスが継承する基底クラスです。テストのライフサイクルを提供します。各テストの前に`setUp()`が呼び出され、各テストの後に`tearDown()`が呼び出されます。setUpはオブジェクトとモックの初期化に使用され、tearDownはリソースのクリーンアップに使用されます。setUpWithErrorおよびtearDownWithErrorを使用すると、各テストでtry-catchを使用せずに初期化エラーを処理できます。
名前が`test`で始まるメソッドは、Xcodeによって自動的にテストとして認識されます。代わりに`@Test`マクロ(Swift Testing)を使用することもできます。テスト名は説明的であるべきです:`testLoginWithValidCredentials`は`testLogin1`よりも優れています。コメントによるテストの文書化は良い習慣ですが、XcodeではUser-Defined Attributesを使用して説明を追加することもできます。
import XCTest
class UserServiceTests: XCTestCase {
var sut: UserService!
var mockSession: MockURLSession!
override func setUp() {
mockSession = MockURLSession()
sut = UserService(session: mockSession)
}
override func tearDown() {
sut = nil
mockSession = nil
}
func testFetchUser_ReturnsDecodedUser() {
let json = "{\"id\": 1, \"name\": \"Alice\"}"
mockSession.setResponse(json)
let user = try await sut.fetchUser(id: 1)
XCTAssertEqual(user.name, "Alice")
}
}
上記の例は、標準的なXCTestCaseの構造を示しています。sut(System Under Test)は、テスト対象のオブジェクトの命名規則です。MockURLSessionが実際のネットワークを置き換え、UserServiceを分離してテストできるようにします。“1テスト1アサーション”の原則によりデバッグが簡素化されます。テストが失敗した場合、開発者はどの機能が壊れているかを即座に把握できます。各XCTestCaseテストは1つのシナリオまたは1つのアサーションを検証する必要があります。
XCTAssertTrueおよびXCTAssertFalseは、ブール値を検証するための基本的なアサーションです。XCTAssertTrue(expression)は、expressionがtrueの場合に合格します。XCTAssertEqualは、Equatableを実装するすべての型をサポートして2つの値の等価性を検証します。浮動小数点数の場合、計算精度を考慮するためにaccuracyパラメータを指定したXCTAssertEqualが使用されます。Google Testing Blog(2024)によると、XCTAssertEqualは標準的なテストスイートのすべてのチェックの70%をカバーしています。
XCTAssertNilおよびXCTAssertNotNilは、オプショナル値のnilをチェックします。これらのアサーションは、オプショナル型が広く使用されているSwiftでは重要です。XCTAssertThrowsErrorは、コードが期待されるエラーをスローすることを検証します。XCTUnwrapは、オプショナル値をアンラップし、値がnilの場合は明確なメッセージとともに失敗するアサーションです。XCTEqualによる文字列比較は、意味論的ではなくリテラル比較を使用します。XCTAssertNoThrowは、コードがエラーをスローしないことを検証するための対になるアサーションです。
| アサーション | 目的 | 例 |
|---|---|---|
| XCTAssertEqual | 等価性の検証 | XCTAssertEqual(a, b) |
| XCTAssertTrue | 真値の検証 | XCTAssertTrue(result) |
| XCTAssertNil | Nilの検証 | XCTAssertNil(error) |
| XCTAssertThrowsError | エラーの検証 | XCTAssertThrowsError(try parse("")) |
| XCTUnwrap | Optionalのアンラップ | XCTUnwrap(value) |
XCTestExpectationは、非同期コードをテストするためのメカニズムです。テストは説明的な名前で期待値を作成し、それを非同期操作に渡し、`wait(for:timeout:)`を呼び出します。タイムアウト内に期待値が満たされない場合、テストは失敗します。タイムアウトのデフォルトは10秒ですが、高速な操作の場合はテスト全体の時間を短縮するために1~3秒に設定することをお勧めします。
XCTWaiterは、wait(for:timeout:)のより柔軟な代替手段です。XCTWaiterを使用すると、複数の期待値を待機し、実行順序を設定し、プログラムでタイムアウトを処理できます。waitとは異なり、XCTWaiterは分析可能な`XCTWaiter.Result`を返します。デリゲートXCTWaiterDelegateは、期待値の順序違反やタイムアウトを通知します。
func testAsyncLogin() {
let expectation = XCTestExpectation(description: "login")
var resultUser: User?
sut.login(email: "a@b.com", password: "123") { user in
resultUser = user
expectation.fulfill()
}
wait(for: [expectation], timeout: 3)
XCTAssertNotNil(resultUser)
XCTAssertEqual(resultUser?.email, "a@b.com")
}
この例では、XCTestExpectationを使用して非同期ログインをテストしています。fulfill()はコールバッククロージャ内で呼び出され、非同期操作が完了したことを通知します。3秒以内にfulfill()が呼び出されない場合、テストはタイムアウトで失敗します。待機が成功した後、アサーションを実行して結果を検証します。複数の期待値は配列として渡し、すべての完了を待機できます。
measure(metrics:)は、パフォーマンステストを作成するためのXCTestCaseメソッドです。measure内のコードブロックは10回連続で実行され、XCTestが統計(平均時間、中央値、標準偏差)を収集します。メトリクスは追跡されるメトリクスの配列です:XCTClockMetric(時間)、XCTMemoryMetric(メモリ)、XCTStorageMetric(ディスク)、XCTCPUMetric(プロセッサ)。Apple WWDC(2024)によると、XCTCPUMetricを使用したパフォーマンステストは、アルゴリズムの回帰を検出するのに役立ちます。
パフォーマンステストのベースラインはXcode Test Planで設定します。実行時間が設定された割合(デフォルト10%)を超えてベースラインを超えた場合、テストは失敗と見なされます。ベースラインは、パフォーマンスの変更が予期されたものであることを確認した後、手動で更新されます。Xcodeのテスト計画を使用すると、デバッグ/リリース、デバイスの種類、iOSバージョンなどの構成ごとにパフォーマンステストをグループ化できます。
func testArraySortPerformance() {
let numbers = (1...10000).shuffled()
measure(metrics: [XCTClockMetric()]) {
let _ = numbers.sorted()
}
}
このパフォーマンステストは、10,000要素の配列のソート時間を測定します。XCTClockMetricは実際の実行時間をキャプチャします。ソートアルゴリズムを変更した後に時間が10%以上増加した場合、テストは回帰を示します。XCTestのパフォーマンステストは特に以下に役立ちます:データ処理アルゴリズム、UIコンポーネントのレンダリング、データベース操作、ネットワークリクエスト。
XCTestのテストプロジェクト構造は慣例に従います:クラスごとに1つのテストファイルを別の`<TargetName>Tests`ディレクトリに配置します。ファイル名は、テスト対象クラスの名前に`Tests`サフィックスを付けたものになります:`UserService.swift` → `UserServiceTests.swift`。XcodeのTest Targetsは単体テストとUIテストで別々に設定され、独立して実行できます。Xcodeのスキームはビルド構成と実行するテストのセットを管理します。
Xcode CloudおよびGitHub Actionsは、`xcodebuild test -scheme App -testPlan SmokeTest`を介したXCTestの実行をサポートしています。CIパイプラインには以下が含まれます:ビルド→単体テストの実行→UIテストの実行→レポートの公開。JUnitレポートは`xcodebuild`の`-resultBundlePath`オプションで生成され、任意のCIツールにインポートできます。コードカバレッジは、テストでカバーされているコード行を表示するXCTestの組み込み機能です。本番コードの最小カバレッジしきい値は、重要なビジネスロジックで70%です。
Xcode CloudおよびGitHub Actionsは、`xcodebuild test -scheme App -testPlan SmokeTest`を介したXCTestの実行をサポートしています。CIパイプラインには以下が含まれます:ビルド→単体テストの実行→UIテストの実行→レポートの公開。JUnitレポートは`xcodebuild`の`-resultBundlePath`オプションで生成され、任意のCIツールにインポートできます。BitriseおよびJenkinsにはXCTest用の既成のステップがあります。
コードカバレッジは、テストでカバーされているコード行を表示するXCTestの組み込み機能です。Xcodeはカバレッジを緑色(カバー済み)、赤色(未カバー)、黄色(部分的にカバー)で表示します。本番コードカバレッジの最小しきい値は、重要なビジネスロジックで70%です。Google Testing Blog(2024)によると、すべてのモジュールに80%のカバレッジを強制すると、ロジックを検証せずにコードを実行するだけの「空のテスト」が発生します。
よくある質問
XCTestは、Xcodeに直接統合されたAppleの公式フレームワークです。QuickとNimbleは、BDD構文とより読みやすいアサーションを提供するサードパーティのライブラリです。QuickとNimbleは受け入れテストに便利ですが、XCTestは外部依存関係がないため単体テストの信頼性が高くなります。
非同期コードは、XCTestExpectation + `wait(for:timeout:)`、またはXCTestの`async/await`メソッド(iOS 13以上)を使用してテストします。コールバックベースのAPIの場合は、クロージャで満たされる期待値を作成します。async/awaitの場合は、非同期関数で標準アサーションを使用します。
XCTestには組み込みのモッキングフレームワークは含まれていません。モッキングはプロトコルを介して実装されます。実際の依存関係と同じプロトコルを実装するモッククラスを作成します。自動モック生成には、Cuckoo、SwiftyMocky、または手動モックを使用します。イニシャライザーを介した依存性注入は、テスト容易性の必須条件です。
はい、XCTestはXcodeまたはxcodebuildを使用して、`-destination 'platform=iOS,name=iPhone 15'`パラメータで実機で実行できます。実機でのUIテストはシミュレーターよりも正確な結果が得られます。デバイスファームで実行するには、BrowserStack、Sauce Labs、またはFirebase Test Labを使用します。
Swift Testing(2024)は、`@Test`、`@Suite`、`@Expect`マクロを備えた新しいAppleフレームワークです。組み込みのテストパラメータ化、スイートグループ化、より読みやすい構文を提供します。Swift TestingはXCTestと共存し、置き換えるものではありません。XCTestは引き続きUIテストとパフォーマンステストの主要なフレームワークです。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。