前言
StoreKit 框架的第二次迭代是我在过去几年中应用程序中最重大的变化。最近版本的 StoreKit 框架已完全采用了 Swift 语言特性,如 async 和 await。本篇内容我们将讨论 StoreKitTest 框架,这不是 StoreKit 2 的一部分,但与之紧密耦合。
StoreKitTest
框架为我们提供了 SKTestSession
类型。使用 SKTestSession
类型的实例,我们可以购买应用内产品、管理交易、退款和过期订阅等。
创建一个 StoreKit Demo
我们从创建一个 StoreKit
相关功能的测试用例开始。我通常有一个称为 SettingsStore
的类型,它定义用户配置并处理应用内购买。我们将使用 StoreKitTest
框架通过测试来覆盖 SettingsStore
的应用内购买管理部分。
Copy code
@MainActor final class StoreKitTests: XCTestCase {
func testProductPurchase() async throws {
let session = try SKTestSession(configurationFileNamed: "SugarBot Food Calorie Counter")
session.disableDialogs = true
session.clearTransactions()
}
}
如上例所示,我们初始化了 SKTestSession
类型的实例。然后,我们调用 clearTransactions
函数来删除我们可能从以前的启动中存储的所有交易。我们还关闭对话框以轻松自动化购买确认流程。
使用 SKTestSession
现在,我们可以使用我们的 SettingsStore
类型来购买产品并处理订阅状态。SKTestSession
类型还允许我们购买一个模拟应用外购买的产品。例如,可能是一个启用了家庭共享的已购买产品。
Copy code
@MainActor final class StoreKitTests: XCTestCase {
var store: SettingsStore!
override func setUp() {
store = SettingsStore()
}
func testProductPurchase() async throws {
let session = try SKTestSession(configurationFileNamed: "SugarBot Food Calorie Counter")
session.disableDialogs = true
session.clearTransactions()
try await session.buyProduct(identifier: "annual")
guard let product = try await Product.products(for: ["annual"]).first else {
return XCTFail("Can't load products...")
}
let status = try await product.subscription?.status ?? []
await store.processSubscriptionStatus(status)
XCTAssertFalse(store.activeSubscriptions.isEmpty)
}
}
如上例所示,我们使用 SKTestSession
类型的 buyProduct
函数来模拟购买。我们还可以使用 SKTestSession
类型的 expireSubscription
函数来过期进行中的订阅,并验证我们的应用程序如何处理这些数据。
Copy code
@MainActor final class StoreKitTests: XCTestCase {
var store: SettingsStore!
override func setUp() {
store = SettingsStore()
}
func testExpiredProduct() async throws {
let session = try SKTestSession(configurationFileNamed: "SugarBot Food Calorie Counter")
session.disableDialogs = true
session.clearTransactions()
let transaction = try await session.buyProduct(identifier: "annual")
let activeProducts = try await Product.products(for: ["annual"])
let activeStatus = try await activeProducts.first?.subscription?.status ?? []
await store.processSubscriptionStatus(activeStatus)
XCTAssertFalse(store.activeSubscriptions.isEmpty)
try session.expireSubscription(productIdentifier: "annual")
let expiredProducts = try await Product.products(for: ["annual"])
let expiredStatus = try await expiredProducts.first?.subscription?.status ?? []
await store.processSubscriptionStatus(expiredStatus)
XCTAssertTrue(store.activeSubscriptions.isEmpty)
}
}
SKTestSession
类型还允许我们使用 refundTransaction
函数模拟产品退款。另一个令人兴奋的选项是测试应用程序对交易更新的反应。
Copy code
let transaction = try await session.buyProduct(identifier: "annual")
// 验证购买 ...
try session.refundTransaction(identifier: UInt(transaction.id))
// 验证退款 ...
askToBuyEnabled 属性
你还可以使用 askToBuyEnabled
属性来启用询问购买功能,然后使用 approveAskToBuyTransaction
或 declineAskToBuyTransaction
函数来批准或拒绝购买。在这种情况下,交易应从挂起更改为成功。
Copy code
session.askToBuyEnabled = true
await store.purchase("annual")
// 验证购买 ...
let declined = store.pendingTrancations.first?.id ?? 0
try session.declineAskToBuyTransaction(identifier: UInt(declined.id))
// 验证购买 ...
await store.purchase("annual")
// 验证购买 ...
let approved = store.pendingTrancations.first?.id ?? 0
try session.approveAskToBuyTransaction(identifier: UInt(approved.id))
// 验证购买 ...
如上例所示,我们使用 SKTestSession
类型的实例来模拟询问购买,并验证我们的应用程序在购买被批准或拒绝时的行为。
总结
本文介绍了如何创建测试用例,然后详细说明了如何使用 SKTestSession 类型来模拟购买、退款和订阅过期等情况,并展示了如何测试应用程序对这些情况的处理。此外,还介绍了使用 askToBuyEnabled 属性启用询问购买功能的方法,并展示了如何验证应用程序对购买被批准或拒绝时的行为。通过这篇文章,读者可以了解如何使用 StoreKitTest 框架来验证应用程序处理应用内购买和用户流程的能力。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。