Swift 中的单元测试 HKSampleQuery
Unit Test HKSampleQuery in Swift
当我需要从 HealthKit
读取数据时,我的代码是这样的:
let stepsCount = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
let stepsSampleQuery = HKSampleQuery(sampleType: stepsCount,
predicate: nil,
limit: 100,
sortDescriptors: nil)
{ [unowned self] (query, results, error) in
if let results = results as? [HKQuantitySample] {
self.steps = results
// Update some UI
}
self.activityIndicator.stopAnimating()
}
healthStore?.executeQuery(stepsSampleQuery)
此特定代码是从 here 中提取的,用于演示目的。
所以我的问题是:
How can I unit test this kind of code ?
我将此代码封装在模型 class 中的一个函数中,该模型 class 对 UI 一无所知。它是这样工作的:
在你拥有
的地方
// Update some UI
调用一个完成闭包,它是使用参数传递给函数的。
您可以像这样从您的控制器 class 调用此函数
hkModel.selectSteps() {
[unowned self] (query, results, error) in
// update UI
}
通过这种方式,您可以将模型 class 中的查询逻辑与 UI 控制器代码完全分开。
现在您可以轻松编写调用相同方法的单元测试:
func testSteps() {
hkModel.selectSteps() {
[unowned self] (query, results, error) in
// XCTAssert(...)
}
}
您最不需要的是尊重您的测试代码是异步调用的:
let stepExpectationEnd = expectationWithDescription("step Query")
hkModel.selectSteps() {
[unowned self] (query, results, error) in
// XCTAssert(...)
stepExpectationEnd.fulfill()
}
waitForExpectationsWithTimeout(10.0) {
(error: NSError?) in
if let error = error {
XCTFail(error.localizedDescription)
}
}
更新
因为你问了:
我在测试设置中处理授权。看起来像这样:
var healthData: HealthDataManager?
override func setUp() {
super.setUp()
healthData = HealthDataManager()
XCTAssert(healthData != nil, "healthDadta must be there")
let authorizationAndAScheduleExpectation = expectationWithDescription("Wait for authorizatiion. Might be manual the first time")
healthData?.authorizeHealthKit({ (success: Bool, error: NSError?) -> Void in
print ("success: \(success) error \(error?.localizedDescription)")
// fails on iPad
XCTAssert(success, "authorization error \(error?.localizedDescription)")
self.healthData?.scheduleAll() {
(success:Bool, error:ErrorType?) -> Void in
XCTAssert(success, "scheduleAll error \(error)")
authorizationAndAScheduleExpectation.fulfill()
}
})
waitForExpectationsWithTimeout(60.0) {
error in
if let error = error {
XCTFail(error.localizedDescription)
}
}
}
第一次在模拟器中运行此代码,您必须手动批准授权。
第一次 运行 测试后 运行 无需人工干预。
当我需要从 HealthKit
读取数据时,我的代码是这样的:
let stepsCount = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
let stepsSampleQuery = HKSampleQuery(sampleType: stepsCount,
predicate: nil,
limit: 100,
sortDescriptors: nil)
{ [unowned self] (query, results, error) in
if let results = results as? [HKQuantitySample] {
self.steps = results
// Update some UI
}
self.activityIndicator.stopAnimating()
}
healthStore?.executeQuery(stepsSampleQuery)
此特定代码是从 here 中提取的,用于演示目的。
所以我的问题是:
How can I unit test this kind of code ?
我将此代码封装在模型 class 中的一个函数中,该模型 class 对 UI 一无所知。它是这样工作的:
在你拥有
的地方// Update some UI
调用一个完成闭包,它是使用参数传递给函数的。
您可以像这样从您的控制器 class 调用此函数
hkModel.selectSteps() {
[unowned self] (query, results, error) in
// update UI
}
通过这种方式,您可以将模型 class 中的查询逻辑与 UI 控制器代码完全分开。
现在您可以轻松编写调用相同方法的单元测试:
func testSteps() {
hkModel.selectSteps() {
[unowned self] (query, results, error) in
// XCTAssert(...)
}
}
您最不需要的是尊重您的测试代码是异步调用的:
let stepExpectationEnd = expectationWithDescription("step Query")
hkModel.selectSteps() {
[unowned self] (query, results, error) in
// XCTAssert(...)
stepExpectationEnd.fulfill()
}
waitForExpectationsWithTimeout(10.0) {
(error: NSError?) in
if let error = error {
XCTFail(error.localizedDescription)
}
}
更新
因为你问了:
我在测试设置中处理授权。看起来像这样:
var healthData: HealthDataManager?
override func setUp() {
super.setUp()
healthData = HealthDataManager()
XCTAssert(healthData != nil, "healthDadta must be there")
let authorizationAndAScheduleExpectation = expectationWithDescription("Wait for authorizatiion. Might be manual the first time")
healthData?.authorizeHealthKit({ (success: Bool, error: NSError?) -> Void in
print ("success: \(success) error \(error?.localizedDescription)")
// fails on iPad
XCTAssert(success, "authorization error \(error?.localizedDescription)")
self.healthData?.scheduleAll() {
(success:Bool, error:ErrorType?) -> Void in
XCTAssert(success, "scheduleAll error \(error)")
authorizationAndAScheduleExpectation.fulfill()
}
})
waitForExpectationsWithTimeout(60.0) {
error in
if let error = error {
XCTFail(error.localizedDescription)
}
}
}
第一次在模拟器中运行此代码,您必须手动批准授权。
第一次 运行 测试后 运行 无需人工干预。