我有一个viewMode,它决定iCloud是启用还是禁用,结果是提示用户是否登录iCloud。
有没有办法从XCTest向iCloud进行渐进式登录/注销,以可靠地测试所有路径?
这是我的测试

func testShowLoginButtonForiCloud() {
  let viewModel = OnboardingViewModel()
  let expectation = XCTestExpectation(description: "Wait for CKContainer auth check")
  var iCloudEnabled: Bool?

  viewModel.shouldShowiCloudLogin { result, error in
    iCloudEnabled = result
    expectation.fulfill()
  }
  wait(for: [expectation], timeout: 5.0)

  XCTAssertNotNil(iCloudEnabled)
  XCTAssertFalse(iCloudEnabled!)
}

这是我的视图模型
typealias Completion = (Bool, Error?) -> Void

final class OnboardingViewModel {
  func shouldShowiCloudLogin(completion: @escaping Completion) {
  CKContainer.default().accountStatus { (status, error) in
    switch status {
      case .available :
        completion(true, nil)
       default :
        completion(false, error)
      }
    }
  }
}

最佳答案

我们能以编程方式登录到CloudKit进行单元测试吗?这是不可取的,因为即使我们可以这样做,测试也将是缓慢而脆弱的。相反,将CloudKit视为架构边界。单元测试可以一直到这个边界。我们可以假装有东西从边界回来。这样,我们可以测试所有路径。
要将此边界编程到代码中,请使用协议。此协议将是一个切片,其中仅包含所需的CKContainer方法。(这是操作中的接口分离原则。)由于CKContainer已经实现了此方法,我们可以将其作为空扩展附加。

protocol CKContainerProtocol {
    func accountStatus(completionHandler: @escaping (CKAccountStatus, Error?) -> Void)
}

extension CKContainer: CKContainerProtocol {}

然后将属性添加到视图模型:
var cloudKitContainer: CKContainerProtocol = CKContainer.default()

默认值意味着您的代码将继续使用真实的CKContainer,除非另有说明。更改代码以调用cloudKitContainer而不是CKContainer.default()
然后在测试代码中,您可以提供CKContainerProtocol的不同实现。这会让你做劈头和嘲笑。您可以确认只调用一次accountStatus()。您可以使用不同的CKAccountStatus值来执行它的闭包,以确认如何调用完成闭包。
(我在iOS Unit Testing by Example: XCTest Tips and Techniques Using Swift中更详细地介绍了类似的内容)

09-27 03:29