Write xctest for function inside an operation queue

155 Views Asked by At

I have a function as follows which adds an operation to the operation queue, how do I test the block of code that is being added to the operation queue, I tried using an expectation by passing an analytics observer spy and check if the value is set but it does not seem to work, please point me in the right direction

func firePendingEvent() {
   for analyticItem in eventQueue {
     eventPriorityQueue.addOperationToQueue (operation: BlockOperation {
          self.analyticsObserver?.logEvent(event: analyticItem) // write test to check the event inside this function
      }, priority: .normal)
      if eventQueue.count > 0 {
        eventQueue.remove(at: 0)
      }
    }    
}  
1

There are 1 best solutions below

0
On

The basic idea is that your test has to create an expectation:

let e = expectation(description: "testGetUsers")

And then when your asynchronous method is done, fulfill that expectation:

e.fulfill()

And then, of course, your test has to, after initiating the asynchronous tasks, wait for the expectations:

waitForExpectations(timeout: 10)

But, obviously, you need to know when your function is done. I would suggest giving it an optional completion handler:

func firePendingEvent(completion: (() -> Void)? = nil) {
    let group = DispatchGroup()

    for analyticItem in eventQueue {
        group.enter()
        eventPriorityQueue.addOperationToQueue(operation: BlockOperation {
            self.analyticsObserver?.logEvent(event: analyticItem)
            group.leave()
        }, priority: .normal)
    }
    eventQueue.removeAll()

    if let completion = completion {
        group.notify(queue: .main, execute: completion)
    }
}

(Note, I personally refrain from mutating an array as I iterate through it, so I add all the tasks to the queue and defer the removeAll until I am no longer iterating.)

Anyway, you can now write your test:

func testFirePendingEvents() {
    let e = expectation(description: "testGetUsers")

    foo.firePendingEvent {
        XCAssert(…) // do whatever tests are appropriate
        e.fulfill()
    }

    waitForExpectations(timeout: 10)
}

But, because that completion handler is optional, and defaults to nil, you don't have to change the rest of your code that isn't availing itself of the new completion handler.

Personally, I would add a parameter to that completion handler closure to pass back whatever value you want to test, but there wasn’t enough in the question to know what result you are testing.