When I need to read data from HealthKit
this is how my code looks like:
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)
This specific code was extracted from here for demo purpose.
So my question is:
How can I unit test this kind of code ?
I encapsulate this code in a function in a model class that knows nothing about the UI. It works like this:
At the place the you have your
// Update some UI
call a completion closure, that was passed to the function using a parameter.
You call this function from your controller class like this
hkModel.selectSteps() {
[unowned self] (query, results, error) in
// update UI
}
This way you have a clean separation between your query logic in the model class and your UIController code.
Now you can easily write a unit test calling the same method:
func testSteps() {
hkModel.selectSteps() {
[unowned self] (query, results, error) in
// XCTAssert(...)
}
}
The last thing you need is to respect that your test code is called asynchronously:
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)
}
}
update
Because you asked:
I handle authorization at the test setup. looks like this:
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)
}
}
}
The first time you run this code in a simulator, you have to approve authorization manually.
After the first run the tests run without manual intervention.