我知道这是一篇旧文章,但 CloudKit 的测试仍然是一个长期存在的问题。我创建了一个模拟框架来测试 CloudKit 的一些主要功能(仅针对 iOS 15.0 及更高版本构建和测试 - 以利用迁移到更清洁的 CKDatabaseOperation 操作)。代码(使用 DocC 完整记录)和测试在这里:https ://github.com/ccavnor/MockCloudKitTesting
以下是如何使用它的示例:
将框架添加到您的项目
下载框架并单击 Xcode 项目的根文件夹以打开项目编辑器。单击您的项目目标并转到“常规”选项卡的“框架、库和嵌入式内容”部分。在此处导入 MockCloudKitFramework。这与 CloudKit 框架应该已经导入到您的项目中的位置相同。
导入框架
在您已导入 CloudKit 的模块中,添加以下导入语句:
import MockCloudKitFramework
添加类型别名
将以下类型别名块添加到模块中。确保将“#if DEBUG”语句保留在那里。它确保类型别名仅在测试和调试运行期间处于活动状态。MockCloudKitFramework 本身被包裹在其中,因此在正常运行时不会错误地访问模拟对象。这些类型别名仅允许您通过它们模拟的 CloudKit 对象的名称来调用 MockCloudKitFramework 模拟对象。
#if DEBUG
typealias CKContainer = MockCKContainer
typealias CKDatabase = MockCKDatabase
typealias CKModifyRecordsOperation = MockCKModifyRecordsOperation
typealias CKFetchRecordsOperation = MockCKFetchRecordsOperation
typealias CKQueryOperation = MockCKQueryOperation
#endif
像使用 CloudKit 一样使用它
func test_CKModifyRecordsOperation_modifyRecordsResultBlock_success() {
// really a MockCKContainer
let cloudContainer: CKContainer = CKContainer.default()
// really a MockCKDatabase
let database: CKDatabase = cloudContainer.publicCloudDatabase
let expect = expectation(description: "CKModifyRecordsOperation")
var records: [CKRecord] = [CKRecord]()
records.append(makeCKRecord())
records.append(makeCKRecord())
// really a MockCKModifyRecordsOperation
let operation = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil)
operation.modifyRecordsResultBlock = { result in
switch result {
case .success:
expect.fulfill()
case .failure(let error):
XCTAssertNil(error) // shouldn't be reached
}
}
database.add(operation)
}
请注意,modifyRecordsResultBlock 被注册为回调,正如您所期望的那样。但是您在 recordsToSave 属性上设置的记录也会添加到 MockCKDatabase - 并且仅添加到您请求的范围(在本例中为 .public):
// really a MockCKFetchRecordsOperation
let operation = CKFetchRecordsOperation(recordIDs: recordIds)
operation.perRecordResultBlock = { recordId, _ in
print("got record with id ---> \(recordId)")
}
database.add(operation)
随心所欲地使用它,你可以使用 CloudKit
这一切都很好,但是模拟测试的真正目的是测试成功(上图)和失败案例。MockCloudKitFramework 允许您在 CKContainer(请参阅文档)或 CKDatabaseOperation 子类上设置我们要测试的错误。假设我们想测试我们的代码如果 CloudKit 没有遇到内部错误会发生什么。我们可以在 CKDatabaseOperation 实例上设置错误
let error = NSError(domain: "CKError", code: CKError.Code.internalError, userInfo: nil)
let operation = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil)
operation.setError = error
或者在类型上设置
CKModifyRecordsOperation.setError = error
但是现在错误将作为失败条件出现在我们的完成块中:
// really a MockCKModifyRecordsOperation
operation.modifyRecordsResultBlock = { result in
switch result {
case .success:
// won't get here
case .failure(let error):
// error contains CKError.internalError code
}
}
// Add the operation to the database to run it
database.add(operation)