Search code examples
objective-cocmock

OCMock confuses references in a threaded environment


I have a piece of code, which is processing a queue synchronously and asynchronously. I'm using OCMock to test the thing and individually I can test both cases (synchronous and asynchronous) but when I test for both at the same time I get trouble.

To verify that the queue is processed correctly I'm passing it a mocked listener and from this listener I'm then asking if it got all the notifications propagated by the queue processor. I have two tests and in the first test (asynchronous) these expectations are met but with the second test (synchronous) I get this error:

OCMockObject[JHQueueListener] : 4 expected methods were not invoked: 
        startedProcessingQueue
        startedToProcessQueueItem:OCMockObject[JHQueueItem]
        finishedProcessingQueueItem:OCMockObject[JHQueueItem]
        finishedProcessingQueue

Here's a link to the project: https://github.com/jphollanti/queue-processor

And here's a link to the test: https://github.com/jphollanti/queue-processor/blob/master/QueueProcessorTests/JHQueueProcessorTests.m


Solution

  • Issue #1: The references are fine but when it comes to the tests, threading is expected to work incorrectly. The problem here is that a new thread is started and in that new thread the status of the queue is set as in progress. But it takes longer to start up a new thread than it does for the main thread to ask for the status and this results in the fact that the queue is not (yet) in progress. Adding a delay of some 10000ms should help things a lot. Like so:

    ...
      [queue processQueueAsynchronously:queueItems];
    
      usleep(10000);
    
      BOOL wentToThread = NO;
      while ([queue isInProgress]) {
        wentToThread = YES;
        ...
      }
    ...
    

    Also, calling dispatch_async(dispatch_queue_t queue, ^(void)block) takes a lot of time and this adds up to the "random" nature of the issues.

    Issue #2: Calling dispatch_async(dispatch_get_main_queue(), ^{ ... } from the main thread causes the block to be sent to some queue which is executed at some time (don't know how it works). This is why the second test (synchronous) failed. Using something like this helps:

    if ([NSThread isMainThread]) {
      for (id <JHQueueListener> listener in listeners) {
        [listener startedToProcessQueueItem:queueItem];
      }
    } else {
      dispatch_async(dispatch_get_main_queue(), ^{
        for (id <JHQueueListener> listener in listeners) {
          [listener startedToProcessQueueItem:queueItem];
        }
      });
    }