Test Storeの概要

0:02:00

RevenueCat Test StoreのiOSセットアップガイドへようこそ!

アプリ内課金のテストは常に困難な作業でした。サンドボックスアカウントの設定、テスト商品の作成、アプリ審査の待機、不安定なネットワーク状況への対応が必要です。RevenueCatのTest Storeは、決定論的で高速かつ信頼性の高いテスト環境を提供することで、これらの課題を解消します。

Test Storeとは?

Test Storeは、App Storeに接続せずにアプリ内課金フローをテストできるRevenueCatの組み込みテスト環境です。すべての新しいRevenueCatプロジェクトで自動的にプロビジョニングされ、購入結果を完全に制御できます。

学習内容

このコードラボでは、RevenueCatのTest Storeをセットアップして使用するための完全なプロセスをガイドします。RevenueCatダッシュボードでTest Storeを有効にし、iOSアプリケーションをTest Store APIキーを使用するように設定します。その後、決定論的な結果で購入フローをテストする方法、アプリ内課金ロジックの自動ユニットテストの作成方法、GitHub Actionsを使用してこれらのテストをCI/CDパイプラインに統合する方法を学びます。

Test Storeの利点

Test Storeはアプリ内課金のテスト方法を革新します。App Store Connectの設定やアプリ承認を待つ必要がある従来のテスト方法とは異なり、Test Storeは即座のテスト機能を提供します。購入結果を完全に決定論的に制御できます:トランザクションが成功するか、失敗するか、キャンセルされるかは完全にあなた次第です。これにより、不安定なネットワーク接続や信頼性の低いサンドボックス環境に対処する必要がなくなります。

真の力は、自動テストを書き始めるときに発揮されます。Test Storeを使用すると、GitHub ActionsなどのCI/CDシステムを含む任意の環境で一貫して実行される、購入ロジックの信頼性の高いユニットテストを構築できます。これにより、数分や数時間ではなく、数秒で購入フローを反復できます。

overview

前提条件

開始する前に、以下を確認してください:

  1. RevenueCatアカウントrevenuecat.comで無料)
  2. RevenueCat SDKを統合したiOSプロジェクト(バージョン5.x以降)
  3. SwiftiOS開発の基本知識
  4. SwiftUIの知識(オプション、UIの例用)

ダッシュボードでTest Storeを有効にする

0:03:00

最初のステップは、RevenueCatダッシュボードでTest Storeを有効にし、Test Store APIキーを取得することです。

ダッシュボードにアクセス

RevenueCatダッシュボードにログインし、左サイドバーメニューのApps & providersセクションに移動します。ここで、接続されているすべてのアプリと利用可能なストア統合を確認できます。

Test Storeを作成

Apps & providersセクションで、利用可能なプロバイダーの中からTest Storeオプションを探します。Test StoreはすべてのRevenueCatプロジェクトで自動的にプロビジョニングされるため、Create Test StoreまたはEnable Test Storeをクリックするだけで有効にできます。セットアップは即座に行われ、承認や設定の同期を待つ必要はありません。

APIキーを取得

Test Storeが有効になったら、アプリリストのTest Storeエントリをクリックして詳細を表示します。ここでTest Store APIキーを見つけることができます。これはtest_プレフィックスで始まります。このキーは通常のRevenueCat APIキーと同じように機能しますが、重要な違いが1つあります:すべての購入リクエストをApp Storeではなく Test Storeにルーティングし、テスト環境を完全に制御できます。

APIキーの分離について: Test Store APIキーは本番環境およびサンドボックスキーとは完全に分離されています。この分離は意図的であり、セキュリティのために重要です。本番ビルドでTest Storeキーを使用しないでください。デバッグおよびテスト環境でのみ使用する必要があります。必要に応じて、異なるテストシナリオ用に複数のTest Store設定を作成できます。

test-store-setup

次のステップ

Test Store APIキーを取得したので、iOSアプリケーションをTest Storeを使用するように設定する準備ができました。

iOSアプリでTest Storeを設定する

0:05:00

iOSアプリケーションをTest Storeを使用するように設定しましょう。重要なのは、テスト実行時に本番APIキーの代わりにTest Store APIキーを使用することです。

ステップ1:Xcode設定でTest Store APIキーを追加

xcconfigファイルを作成するか、Xcodeビルド設定を使用して設定ごとにAPIキーを管理します。Debug.xcconfigという名前のファイルを作成します:

text
// Debug.xcconfig
REVENUECAT_API_KEY = test_YOUR_KEY_HERE

そしてRelease.xcconfig

text
// Release.xcconfig
REVENUECAT_API_KEY = appl_YOUR_PRODUCTION_KEY

次に、Info.plistREVENUECAT_API_KEYを追加します:

xml
<key>RevenueCatAPIKey</key>
<string>$(REVENUECAT_API_KEY)</string>

ステップ2:アプリでSDKを初期化

@main App構造体を更新して、適切なAPIキーでRevenueCatを設定します:

swift
import RevenueCat
import SwiftUI

@main
struct MyApp: App {
    init() {
        // Info.plistからAPIキーを読み取る(xcconfigで設定)
        let apiKey = Bundle.main.infoDictionary?["RevenueCatAPIKey"] as? String
            ?? "test_YOUR_KEY_HERE"

        Purchases.configure(withAPIKey: apiKey)

        // テストビルドでデバッグログを有効化
        #if DEBUG
        Purchases.logLevel = .debug
        #endif
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

ステップ3:環境固有の設定

より高度なセットアップでは、#if DEBUGプリプロセッサディレクティブを使用して設定ヘルパーを作成できます:

swift
enum RevenueCatConfig {
    static var apiKey: String {
        #if DEBUG
        return "test_YOUR_KEY_HERE"
        #else
        return "appl_YOUR_PRODUCTION_KEY"
        #endif
    }

    static func configure() {
        Purchases.configure(withAPIKey: apiKey)

        #if DEBUG
        Purchases.logLevel = .verbose
        #endif
    }
}

アプリの初期化で使用します:

swift
@main
struct MyApp: App {
    init() {
        RevenueCatConfig.configure()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

検証

Test Storeが動作していることを確認するには:

  1. Xcodeでデバッグモードでアプリを実行
  2. Xcodeコンソールで"Purchases SDK initialized with Test Store"を確認
  3. SDKがApp Storeではなく Test Storeに接続されていることを示します
test-store-config

購入フローをインタラクティブにテストする

0:05:00

Test Storeを有効にすると、結果を完全に制御して購入フローをテストできます。Test Storeが購入ダイアログを表示すると、あなたが結果を決定します。

Test Store購入ダイアログを理解する

Test Storeで購入を開始すると、標準のApp Store購入シートではなく、カスタムのTest Storeダイアログが表示されます。このダイアログは実際の購入ダイアログと同様に商品の詳細と価格を表示しますが、重要な違いが1つあります:結果を制御できます。ダイアログには3つの明確なオプションが表示されます:完了したトランザクションをシミュレートする購入成功、支払い失敗をテストする購入失敗、ユーザーのキャンセルをシミュレートするキャンセル。この決定論的な動作がTest Storeをテストに非常に強力にしています。

成功フローのテスト

成功した購入フローをテストしましょう:

swift
func purchaseProduct() async {
    do {
        // Test Storeから商品を取得
        let offerings = try await Purchases.shared.offerings()

        guard let package = offerings.current?.availablePackages.first else {
            return
        }

        // 購入を開始
        let (_, customerInfo, _) = try await Purchases.shared.purchase(package: package)

        // 結果を確認
        let isPremium = customerInfo.entitlements["premium"]?.isActive == true

        if isPremium {
            // 購入成功!
            showPremiumContent()
        }
    } catch {
        // エラーを処理
        handlePurchaseError(error)
    }
}

ハッピーパスのテスト: アプリを実行し、通常どおり購入フローをトリガーします。Test Storeダイアログが表示されたら、「購入成功」をタップします。アプリは即座にプレミアムアクセスを付与し、Entitlementが正しく更新されたことを確認できます。これにより、実際の支払い方法やサンドボックスアカウントなしで、成功フローが正しく動作することを素早く検証できます。

失敗シナリオのテスト

アプリが失敗を処理する方法をテストします:

swift
func handlePurchaseError(_ error: Error) {
    guard let purchasesError = error as? RevenueCat.ErrorCode else {
        showError("Unexpected error: \(error.localizedDescription)")
        return
    }

    switch purchasesError {
    case .purchaseCancelledError:
        // ユーザーがキャンセル - エラーを表示しない
        print("User cancelled purchase")
    case .purchaseInvalidError:
        // 無効な購入
        showError("This purchase is not available")
    case .paymentPendingError:
        // 支払い保留中(例:親の承認待ち)
        showPendingMessage()
    default:
        // その他のエラー
        showError("Purchase failed: \(error.localizedDescription)")
    }
}

エラーハンドリングのテスト: 購入フローを再度トリガーしますが、今回はTest Storeダイアログが表示されたら、「購入失敗」をタップします。アプリはエラーを適切に処理し、ユーザーに適切なエラーメッセージを表示する必要があります。ユーザーがプレミアムアクセスを受けておらず、アプリの状態が一貫していることを確認します。これは、本番環境でエラーハンドリングロジックが正しく動作することを確認するために重要です。

キャンセルのテスト

swift
struct PaywallView: View {
    @State private var isPurchasing = false
    var onDismiss: () -> Void

    var body: some View {
        Button(action: {
            Task {
                isPurchasing = true
                do {
                    try await purchaseProduct()
                } catch let error as RevenueCat.ErrorCode
                    where error == .purchaseCancelledError {
                    // ユーザーがキャンセル - 単に閉じる
                    onDismiss()
                } catch {
                    // その他のエラーを処理
                    handlePurchaseError(error)
                }
                isPurchasing = false
            }
        }) {
            Text(isPurchasing ? "Processing..." : "Subscribe")
        }
        .disabled(isPurchasing)
    }
}

ユーザーキャンセルのテスト: ペイウォールを開き、購読ボタンをタップします。Test Storeダイアログが表示されたら、「キャンセル」をタップしてユーザーが購入を中止することをシミュレートします。ペイウォールはエラーメッセージを表示せずにきれいに閉じる必要があります(キャンセルはエラー状態ではなく、通常のユーザーアクションです)。購入が記録されておらず、アプリの状態が変更されていないことを確認します。

重要なポイント

Test Storeの美しさは、その決定論的な制御にあります。各購入試行で何が起こるかを正確に決定します。これは、数分で成功、失敗、キャンセルのすべてのシナリオを徹底的にテストできることを意味し、実際の支払い方法やサンドボックスアカウントの遅延や複雑さに対処する必要がありません。App Storeと統合する準備ができるまでに、購入処理ロジックが堅固であるという確信を持てます。

test-store-testing

自動ユニットテストを書く

0:08:00

Test Storeの最も強力な機能の1つは、アプリ内課金ロジックの自動ユニットテストを可能にすることです。CI/CDで信頼性高く実行されるテストを書きましょう。

ステップ1:XCTestをセットアップ

XCTestはXcodeに組み込まれているため、外部依存関係は不要です。プロジェクトにテストターゲットがあることを確認してください。Xcodeのデフォルトプロジェクトテンプレートを使用した場合は、すでに含まれています。Package.swiftまたはXcodeプロジェクトにはすでにRevenueCatが依存関係として含まれているはずです:

swift
// Package.swiftまたはXcode SPM経由
dependencies: [
    .package(url: "https://github.com/RevenueCat/purchases-ios.git", from: "5.58.0")
]

ステップ2:購入リポジトリプロトコルを作成

まず、Swiftプロトコルを使用してテスト可能な購入リポジトリを作成しましょう:

swift
protocol PurchaseRepository {
    func getOfferings() async throws -> Offerings
    func purchase(package: Package) async throws -> CustomerInfo
    func getCustomerInfo() async throws -> CustomerInfo
}

final class PurchaseRepositoryImpl: PurchaseRepository {

    func getOfferings() async throws -> Offerings {
        return try await Purchases.shared.offerings()
    }

    func purchase(package: Package) async throws -> CustomerInfo {
        let (_, customerInfo, _) = try await Purchases.shared.purchase(package: package)
        return customerInfo
    }

    func getCustomerInfo() async throws -> CustomerInfo {
        return try await Purchases.shared.customerInfo()
    }
}

ステップ3:ビジネスロジックを含むViewModelを作成

swift
enum PaywallState {
    case loading
    case success(packages: [Package])
    case purchasing
    case purchaseSuccess
    case purchaseCancelled
    case error(message: String)
}

@MainActor
final class PaywallViewModel: ObservableObject {
    @Published var state: PaywallState = .loading

    private let repository: PurchaseRepository

    init(repository: PurchaseRepository) {
        self.repository = repository
    }

    func loadProducts() async {
        do {
            let offerings = try await repository.getOfferings()
            let packages = offerings.current?.availablePackages ?? []
            state = .success(packages: packages)
        } catch {
            state = .error(message: error.localizedDescription)
        }
    }

    func purchase(package: Package) async {
        state = .purchasing
        do {
            let customerInfo = try await repository.purchase(package: package)
            let isPremium = customerInfo.entitlements["premium"]?.isActive == true

            if isPremium {
                state = .purchaseSuccess
            } else {
                state = .error(message: "Purchase completed but entitlement not active")
            }
        } catch let error as RevenueCat.ErrorCode
            where error == .purchaseCancelledError {
            state = .purchaseCancelled
        } catch {
            state = .error(message: error.localizedDescription)
        }
    }
}

ステップ4:XCTestでユニットテストを書く

モックリポジトリを使用してユニットテストを作成します:

swift
import XCTest
@testable import MyApp

final class MockPurchaseRepository: PurchaseRepository {
    var mockOfferings: Offerings?
    var mockCustomerInfo: CustomerInfo?
    var mockError: Error?

    func getOfferings() async throws -> Offerings {
        if let error = mockError { throw error }
        return mockOfferings!
    }

    func purchase(package: Package) async throws -> CustomerInfo {
        if let error = mockError { throw error }
        return mockCustomerInfo!
    }

    func getCustomerInfo() async throws -> CustomerInfo {
        if let error = mockError { throw error }
        return mockCustomerInfo!
    }
}

@MainActor
final class PaywallViewModelTests: XCTestCase {

    private var mockRepository: MockPurchaseRepository!
    private var viewModel: PaywallViewModel!

    override func setUp() async throws {
        mockRepository = MockPurchaseRepository()
        viewModel = PaywallViewModel(repository: mockRepository)
    }

    func testPurchaseSuccess() async {
        // Given: アクティブなEntitlementで成功した購入をモック
        // アクティブな"premium" Entitlementを持つmockCustomerInfoを設定

        // When: 購入が開始される
        // await viewModel.purchase(package: mockPackage)

        // Then: 状態がpurchaseSuccessであるべき
        // XCTAssertEqual(viewModel.state, .purchaseSuccess)
    }

    func testPurchaseCancelled() async {
        // Given: キャンセルされた購入をモック
        mockRepository.mockError = RevenueCat.ErrorCode.purchaseCancelledError

        // When: 購入が開始される
        // await viewModel.purchase(package: mockPackage)

        // Then: 状態がpurchaseCancelledであるべき
        // XCTAssertEqual(viewModel.state, .purchaseCancelled)
    }

    func testPurchaseFailed() async {
        // Given: 失敗した購入をモック
        mockRepository.mockError = RevenueCat.ErrorCode.paymentPendingError

        // When: 購入が開始される
        // await viewModel.purchase(package: mockPackage)

        // Then: 状態がエラーであるべき
        // if case .error(let message) = viewModel.state {
        //     XCTAssertTrue(message.contains("pending"))
        // } else {
        //     XCTFail("Expected error state")
        // }
    }
}

ステップ5:モックベースのテスト代替案

さらに制御するために、完全にモック化されたアプローチを使用できます:

swift
final class SpyPurchaseRepository: PurchaseRepository {
    var getOfferingsCalled = false
    var purchaseCalled = false
    var lastPurchasedPackage: Package?

    var stubbedOfferings: Offerings!
    var stubbedCustomerInfo: CustomerInfo!
    var stubbedError: Error?

    func getOfferings() async throws -> Offerings {
        getOfferingsCalled = true
        if let error = stubbedError { throw error }
        return stubbedOfferings
    }

    func purchase(package: Package) async throws -> CustomerInfo {
        purchaseCalled = true
        lastPurchasedPackage = package
        if let error = stubbedError { throw error }
        return stubbedCustomerInfo
    }

    func getCustomerInfo() async throws -> CustomerInfo {
        if let error = stubbedError { throw error }
        return stubbedCustomerInfo
    }
}

このアプローチが機能する理由

このテストアプローチは、アプリ内課金テストの従来の課題を解消します。Test Storeにはネットワーク依存関係がないため、テストは毎回信頼性高く実行されます。接続の問題による不安定な失敗はもうありません。テストは数分ではなく数秒で実行され、開発中に素早いフィードバックが得られます。実際の支払いシステムでは再現が困難または時間のかかるエッジケースを含む、すべてのシナリオで完全なテストカバレッジを達成できます。

私たちが構築したアーキテクチャ(ビジネスロジックをSDKから分離するプロトコルパターン)により、これらのテストは高速でメンテナンスしやすくなっています。GitHub Actionsなどの任意のCI/CD環境で実行でき、本番環境に到達する前にバグを発見できます。

CI/CDでテストを実行する

0:04:00

GitHub Actionsでテストを自動的に実行するように設定し、コードの変更ごとに購入ロジックの信頼性を確保しましょう。

ステップ1:GitHub Actionsワークフローを作成

.github/workflows/ios-tests.ymlを作成します:

yaml
name: iOS Tests

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  test:
    runs-on: macos-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Select Xcode version
        run: sudo xcode-select -s /Applications/Xcode.app

      - name: Cache SPM packages
        uses: actions/cache@v3
        with:
          path: |
            ~/Library/Developer/Xcode/DerivedData
            .build
          key: ${{ runner.os }}-spm-${{ hashFiles(&#039;**/Package.resolved') }}
          restore-keys: |
            ${{ runner.os }}-spm-

      - name: Run unit tests
        env:
          REVENUECAT_TEST_STORE_API_KEY: ${{ secrets.REVENUECAT_TEST_STORE_API_KEY }}
        run: |
          xcodebuild test \
            -scheme MyApp \
            -destination &#039;platform=iOS Simulator,name=iPhone 15,OS=latest' \
            -resultBundlePath TestResults.xcresult \
            REVENUECAT_API_KEY=$REVENUECAT_TEST_STORE_API_KEY

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: test-results
          path: TestResults.xcresult

ステップ2:Test Store APIキーをGitHub Secretsに追加

  1. GitHubリポジトリに移動
  2. SettingsSecrets and variablesActionsに移動
  3. New repository secretをクリック
  4. Name: REVENUECAT_TEST_STORE_API_KEY
  5. Value: Test Store APIキー(例:test_xxxxx
  6. Add secretをクリック

ステップ3:XcodeでSecretを使用するように設定

xcconfigを更新して、利用可能な場合に環境変数を読み取るようにします:

text
// Debug.xcconfig
// 利用可能な場合は環境変数を使用(CI)、そうでなければローカルキーにフォールバック
REVENUECAT_API_KEY = $(REVENUECAT_API_KEY:default=test_YOUR_KEY_HERE)

ステップ4:Fastlane代替案

iOS CI/CDにFastlaneを使用している場合、テストレーンを追加できます:

ruby
lane :test do
  run_tests(
    scheme: "MyApp",
    devices: ["iPhone 15"],
    result_bundle: true,
    xcargs: "REVENUECAT_API_KEY=#{ENV['REVENUECAT_TEST_STORE_API_KEY']}"
  )
end

セットアップを確認

変更をコミットしてプッシュし、ワークフローをトリガーします。リポジトリのGitHub Actionsタブに移動して、テストが自動的に実行されるのを確認します。ワークフローがコードをチェックアウトし、macOS環境をセットアップし、テストスイートを実行するリアルタイムの進捗状況が表示されます。数分以内に、すべてのテストがCI環境で合格するかどうかがわかります。

自動テストの力

これがセットアップされると、すべてのプルリクエストがマージ前に自動的に検証されます。すべてのシナリオを手動でテストする必要なく、変更が購入ロジックを壊した場合に即座にフィードバックが得られます。Test Storeは、すべての実行で一貫した結果を生成し、不安定なテストの苛立ちを解消します。これにより、バグのあるコードがメインブランチに到達するのを防ぐ品質ゲートが作成され、繰り返しの手動テストに費やされる無数の時間を節約できます。

ベストプラクティスと本番環境セットアップ

0:03:00

Test Storeをセットアップしたので、ベストプラクティスとTest Storeと本番環境間の移行方法について説明しましょう。

環境の分離

常に#if DEBUGを使用してTest Storeと本番キーを分離してください:

swift
enum RevenueCatKeys {
    // Test Store - テスト専用
    static let testStoreKey = "test_xxxxx"

    // App Store - 本番用
    static let appStoreKey = "appl_xxxxx"

    static var apiKey: String {
        #if DEBUG
        return testStoreKey
        #else
        return appStoreKey
        #endif
    }
}

Xcodeスキーム/設定ベースのキー切り替え

Xcodeビルド設定を使用してより細かい制御を行います:

text
// Debug.xcconfig (Test Store)
REVENUECAT_API_KEY = test_xxxxx
USE_TEST_STORE = YES

// Staging.xcconfig (Test Store)
REVENUECAT_API_KEY = test_xxxxx
USE_TEST_STORE = YES

// Release.xcconfig (App Store)
REVENUECAT_API_KEY = appl_xxxxx
USE_TEST_STORE = NO

包括的なテスト戦略の構築

アプリ内課金の堅牢なテスト戦略は、ピラミッドアプローチに従うべきです。まず、Test Storeを使用したユニットテストで購入ロジックを分離して検証します。これらは高速に実行され、ほとんどの問題を早期に発見します。その基盤の上に、同じくTest Storeを使用した統合テストを構築し、完全な購入フローをエンドツーエンドで検証します。実装に自信が持てたら、Apple Sandboxでの手動テストに移行してプラットフォーム統合を確認します。最後に、実際の支払いで小規模な本番テストを行い、ライブ環境ですべてが動作することを確認します。

Test StoreとApple Sandboxの選択

初期開発と反復中は、Test Storeが最良の友です。購入フローの迅速なプロトタイピング、ユニットテストと統合テストの作成、エラーハンドリングとエッジケースのテストに最適です。即座のフィードバックループにより、信頼性の高い高速な結果が必要なCI/CD自動テストに理想的です。

Test Storeがシミュレートしないプラットフォーム固有の機能をテストする必要がある場合は、Apple Sandboxに切り替えます。これには、保留中のトランザクション(Ask to Buy承認待ちなど)、レシート検証の詳細、サブスクリプション更新サイクル、地域固有の価格設定が含まれます。Sandboxは本番前の検証環境と考えてください。コアロジックが堅固で、App Store統合を確認する必要があるときに使用します。

セキュリティのベストプラクティス

本番環境でTest Store APIキーを公開しないでください:

swift
// ❌ 悪い例:ハードコードされたキー
let apiKey = "test_xxxxx"

// ✅ 良い例:コンパイル時の設定
#if DEBUG
let apiKey = "test_xxxxx"
#else
let apiKey = "appl_xxxxx"
#endif

// ✅ より良い例:xcconfig経由でInfo.plistから読み取る
let apiKey = Bundle.main.infoDictionary?["RevenueCatAPIKey"] as! String

ロギングとデバッグ

適切なログレベルを有効にします:

swift
#if DEBUG
Purchases.logLevel = .verbose
print("RevenueCat: Using Test Store for testing")
#else
Purchases.logLevel = .info
#endif

本番への移行

アプリをリリースする前に、徹底的なチェックリストを完了していることを確認してください。すべてのテストがTest Storeで合格し、Apple Sandboxで統合を手動で確認しておく必要があります。Xcode設定が各環境で正しいAPIキーを使用するように適切に設定されていることを再確認してください。本番APIキーはセキュリティで保護され、バージョン管理にコミットされていない必要があります。Test Storeキーがリリースビルドから完全に削除されていることを確認してください。デバッグ設定にのみ存在する必要があります。ロギングが適切に設定されていることを確認し(デバッグでは詳細、本番では最小限)、エラーハンドリングがすべてのシナリオでテストされていることを確認してください。

まとめ

おめでとうございます!🎉 iOSでRevenueCatのTest Storeを正常にセットアップしました。このコードラボを通じて、RevenueCatダッシュボードでTest Storeを有効にする方法、iOSアプリケーションでTest Store APIキーを設定する方法、決定論的な結果で購入フローをテストする方法、購入ロジックの自動ユニットテストを作成する方法、GitHub ActionsなどのCI/CD環境でこれらのテストを実行する方法、環境分離のベストプラクティスを学びました。

重要なポイント

Test Storeは、アプリ内課金テストへのアプローチを根本的に変えます。サンドボックスアカウントと格闘したり、ネットワーク依存の不安定なテストに対処する日々は終わりました。購入結果を決定論的に制御することで、実装に真の自信を与える信頼性の高いテストを作成できます。CI/CD統合により、購入ロジックはすべてのコード変更で継続的に検証され、数時間ではなく数秒で購入フローを反復できます。最も重要なのは、購入関連のバグが本番環境に到達する前に発見でき、収益とユーザーエクスペリエンスの両方を保護できることです。

次のステップ

Test Storeをセットアップしたので、この基盤の上に構築する時です。すべての購入シナリオをカバーする包括的なテストを書き始めてください。ハッピーパスだけでなく、すべてのプルリクエストで自動的に実行されるようにCI/CDパイプラインに統合してください。異なる失敗モードでエラーハンドリングを徹底的にテストし、アプリが適切に劣化することを確認してください。準備ができたら、サブスクリプション更新などを確認するためにApple Sandboxに移行してプラットフォーム固有のテストを行ってください。最後に、購入ロジックがすべてのステップで徹底的に検証されていることを知って、自信を持ってリリースしてください。

追加リソース

RevenueCat Test Storeブログ記事 RevenueCat iOS SDKドキュメント テストガイド GitHub: purchases-ios

Happy testing! 🚀