Always Objective C asynchronous operation more async when in async test

#concurrency#asynchronous#unittest#objective c
at

TL;DR

Always Use - (void)XCA_notify:(XCTAsyncTestCaseStatus)status withDelay:(NSTimeInterval)delay in async test.

Do async.

Most opeartion can be an async behavior in objective C. So, you code become js-ed.

dispatch_async(dispatch_get_global_queue(<#dispatch_queue_priority_t priority#>, <#unsigned long flags#>), ^(void) {
// bluhbluh codes
  dispatch_async(dispatch_get_main_queue(), ^(void) {
  // come back codes
  });
});

From Objective C's block I got Why js is called the C's next generation. And, you want to write test? Welcome to pandora.

Some operation is too fast to async.

For better consistency reason, I use XCAsyncTestCase to do async tests, insert [self XCA_notify:XCTAsyncTestCaseStatusSucceeded]; and wait in the end of the test [self XCA_waitForStatus:XCTAsyncTestCaseStatusSucceeded timeout:1]; BUT, if you run test like this:

- (void)testAsyncWhatWhat {
    [self XCA_notify:XCTAsyncTestCaseStatusSucceeded];
    [self XCA_waitForStatus:XCTAsyncTestCaseStatusSucceeded timeout:1];
}

Timeout Exception!

use the following instead:

- (void)testAsyncWhatWhat
{
  [self XCA_notify:XCTAsyncTestCaseStatusSucceeded withDelay:0.01];
  [self XCA_waitForStatus:XCTAsyncTestCaseStatusSucceeded timeout:1];
}

Because this operation is so fast. For an illustration, if you fetch something from core data, Its will raise an exception. Your code don't raise? Good, you got a heinsensbug.

Internal of AsyncTestCase

XCA set two property to check async status

  • _notified It will set _notified to no at first when call XCA_waitForStatus:timeout:. Set _notified to no means to initialize the next async test. It helps to test async status multiple times. if XCA_notify: called before XCAwaitForStatus:timeout:, Then XCAwaitForStatus:timeout: will initialize at wrong time, and can't detect async.
  • _expectedStatus

Concurrency bug is always about time!

« Quote post