问题
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
.
Inspired by this tutorial (use XCTestExpectation), I tried the following way:
-(void) testCheckData {
// partial mock MyService
id myService = [OCMockObject partialMockForObject:[MyService getInstance]];
[myService startMe];
// function to test
[myService doTask];
// I setup expectation
XCTestExpectation *expectation = [self expectationWithDescription:@"data checked"];
// run assertion asynchronisely
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
XCTAssertTrue([self isDataChecked]);
// expectation fulfill
[expectation fulfill];
});
// wait for 5 seconds
[self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
if (error) {
NSLog(@"Timeout Error: %@", error);
}
}];
}
However, when I run my test, the waitForExpectationsWithTimeout:handler: doesn't work, I mean it doesn't wait for 5 seconds, the assertion part run immediately after function under test get called. Why it doesn't wait 5 seconds?
====== UPDATE ======
I also tried not using async block :
-(void) testCheckData {
// partial mock MyService
id myService = [OCMockObject partialMockForObject:[MyService getInstance]];
[myService startMe];
// function to test
[myService doTask];
// I setup expectation
XCTestExpectation *expectation = [self expectationWithDescription:@"data checked"];
// run assertion
XCTAssertTrue([self isDataChecked]);
// expectation fulfill
[expectation fulfill];
// wait for 5 seconds
[self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
if (error) {
NSLog(@"Timeout Error: %@", error);
}
}];
}
But I still get the same problem, there is no waiting 5 seconds, the test return immediately, why?
===== AND =====
I might be wroing, if we ignore my update above & back to see my original code using async block.I think waitForExpectations:5
should do the waiting that I don't need to use while
loop, why I think this way is because of that tutorial.
If we check that tutorial, it firstly show the old stye of using while loop to wait, then, it changed to expectation style which doesn't use any while loop, what it does is set expectation->start work ( assert in the completion block of its work), it also has the waitForExpectations:
code, which looks exactly the same as my code with the async block. I want to understand why my original code looks the same as the tutorial but doesn't work. Do I miss something?
回答1:
As soon as you begin to waitForExpectations, your dispatch_async will get a chance to run. It will assert that your data have been checked, and then -- regardless of whether your data has been checked or not -- it marks the expectation as fulfilled. Once fulfilled, we no longer need to wait and we can finish.
That's probably not what you want to do, but it's what the code says.
回答2:
OK, solved.
I should use dispatch_after
insteand of dispatch_async
, because I don't have a completion block in my function under test while the example in that tutorial has one. So, I need to change to this:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
XCTAssert(...);
[expectation fulfill];
});
If you want to use dispatch_async
please check @Avi's comment, use a while
loop in the async block to wait.
来源:https://stackoverflow.com/questions/37845892/unit-test-async-function-by-using-xctestexpectation-but-it-doesnt-wait-for-the