启用和禁用iCloud的XCTest

时间:2019-01-17 16:48:40

标签: swift unit-testing icloud cloudkit xctest

我有一个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!)
}

这是我的ViewModel

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)
      }
    }
  }
}

1 个答案:

答案 0 :(得分:1)

我们可以以编程方式登录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 book I'm currently writing中进行深入介绍。)