概要
StoreKitTestを使って、課金キャンセル処理のユニットテストを書きたかった。
最初利用しようと思っていたSKTestSession
のプロパティがDeperecatedになっていたので、別の方法で実装しようとした。
→少し詰まったので、その備忘録
実装(Deperecated)
課金処理を管理するPurchaseManager
クラスでは、以下のように、支払い処理の結果に応じてStateを切り替えているので、caseが.userCancelled
になった時のstateをチェックするテストを書く必要がある。
func purchase(_ product: Product) async {
do {
let result = try await product.purchase()
switch result {
case let .success(.verified(transaction)):
await transaction.finish()
await self.updatePurchasedProducts()
state = .success
case .success(.unverified(_, _)), .pending:
state = .idle
break
case .userCancelled:
state = .failure(.canceled)
break
@unknown default:
state = .failure(.unknown)
break
}
} catch {
state = .failure(.unknown)
}
}
そこで購入キャンセル時のテストケースを以下のように書いた。
final class PurchaseManagerTest: XCTestCase {
var session: SKTestSession!
var purchaseManager: PurchaseManager!
// setUpは記載省略
func testPurchaseCancellation() async throws {
// 購入キャンセルの設定
session.failTransactionsEnabled = true // deperecated
session.failureError = SKError.paymentCancelled //deperecated
try await purchaseManager.loadProducts()
let product = purchaseManager.product
await purchaseManager.purchase(purchaseManager.product)
// 購入がキャンセルされたことを確認
XCTAssertEqual(purchaseManager.state, .failure(.canceled))
}
}
が、sessionをキャンセル状態に設定するところで、iOS17以降でdeperecatedになっている旨の警告が出た。
(ドキュメント)
そもそもテストも通らなかったので、SKError.paymentCancelled
を指定しているのが間違っている様子。(単純に理解が浅かった)
と言うわけで、別のコードに書き換えを実施。
実装(修正版)
simulatedError(forAPI:)
を使えと警告が出ていたが、課金キャンセル処理を設定したい場合使うのはsetSimulatedError<API>(API.Failure?, forAPI: API)
の方だった。
WWDC2023の動画に解説があったのでそちらを元に修正。
修正後のテストケースはこちら。
final class PurchaseManagerTest: XCTestCase {
var session: SKTestSession!
var purchaseManager: PurchaseManager!
// setUpは記載省略
func testPurchaseCancellation() async throws {
// 購入キャンセルの設定
try await session.setSimulatedError(
StoreKitPurchaseAPI.Failure.generic(.userCancelled),
forAPI: StoreKitPurchaseAPI.purchase
)
try await purchaseManager.loadProducts()
let product = purchaseManager.product
await purchaseManager.purchase(purchaseManager.product)
// 購入がキャンセルされたことを確認
XCTAssertEqual(purchaseManager.state, .failure(.canceled))
}
}
無事テストも通るようになり、めでたしめでたし。
genericがピンと来なかったので、型を明示的に書いているけれど、もちろん以下のように型省略でも書けます。
try await session.setSimulatedError(.generic(.userCancelled), forAPI: .purchase)
終わりに
やはり公式を見るのが一番だったのだけれど、ドキュメントは説明これしか書いていなかったので、もうちょっとリファレンス充実させてほしい気もする...
わかれば簡単だったが、意外と参考資料がなくて少し詰まったところなので、本記事が参考になれば幸いです。