Reputation: 42602
I have a MyService
class which inherits NSThread
:
header:
@interface MyService : NSThread {
-(void) startMe;
-(void) doTask;
...
}
implementation:
@implementation MyService
-(void)startMe {
[self start];
}
-(void) doTask {
[self performSelector:@selector(checkData:) onThread:self withObject:nil waitUntilDone:YES];
}
-(void) checkData {
...
// NOTE: dataChecked is an instance variable.
dataChecked = YES;
}
@end
I want to unit test the above -(void)doTask
and verify that -(void)checkData
is really called. I use OCMock library to partially mock MyService
& tried the following way:
-(void) testCheckData {
// partial mock MyService
id myService = [OCMockObject partialMockForObject:[MyService getInstance]];
[myService startMe];
// function to test
[myService doTask];
// I wait 2 seconds for checkData to finish its work
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^(void){
// When running this test case, these code is never called & test passed successfully, WHY?
NSLog(@"Check if checkData does its work");
XCTAssertTrue([self isDataChecked]);
});
}
Two questions:
Q1. As you see above, in my test case, I call -(void)doTask
& wait 2 seconds, expect the -(void)checkData
is executed, then assert the result, but when running test case, it doesn't run the block in dispatch_after(...)
& test passed successfully, WHY it doesn't run the block?
Q2. What is the right way to unit test a function like -(void)doTask
which invokes NSObject#performSelector:onThread:withObject:waitUntilDone:?
Upvotes: 0
Views: 794
Reputation: 7552
Q1
If you can't change doTask
to make it testable, you have to use an XCTestExpectation
to wait for the test to complete. Also, instead of waiting a preset amount of time, you should busy-loop, checking the variable on each iteration. You can set a timeout on the expectation to trigger a failure after some reasonable amount of time.
// setup expectation
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (dataChecked == NO);
// signal expectation
});
// wait for expectation
Q2
If you have to know the implementation of the method to successfully test it, it's not a good candidate for unit testing. It shouldn't matter how the method being tested invokes other methods. It only matters if side-effects are synchronous- or asynchronously completed.
Upvotes: 0