Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to test asynchronous methods Swift

Tags:

swift

I have following class that I want to run from unites:

class WmBuildGroupsTask{

init(){}

    func doInBackground() -> WmTransferItem{
         NSThread.sleepForTimeInterval(20)// sleep 20 sec to simulate long task
                            }

    func onPostExecute(transferItem:WmTransferItem){        
        //called when long task finished        
    }


    func execute(){        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {

            var  transItem:WmTransferItem = self.doInBackground()

            dispatch_async(dispatch_get_main_queue(), {
                self.onPostExecute(transItem)
                });
            });                
      }    
}

I tried to run from Unitest file:

var task:WmBuildGroupsTask = WmBuildGroupsTask()
task.execute(); 

but test is done before doInBackground() method finished.

How to make it work?

like image 571
snaggs Avatar asked Dec 14 '22 20:12

snaggs


1 Answers

You can achieve that by using XCTestExpectation(since Xcode 6).

How it works:

We create XCTestExpectation instance that work like timer. Your test will never finish till one of both cases happend:

  • XCTestExpectation.fulfill() called
  • you got timeout defined with waitForExpectationsWithTimeout and therefore test will fail

How to use XCTestExpectation

Step 1

Create new protocol for class under test (in your case WmBuildGroupsTask):

protocol MyCallback{
    func onDone(results: String)
}

This is our callback.

Step 2

in Unitest inherit this protocol:

class Test_WmBuildGroupsTask : XCTestCase, MyCallback {
/* ...*/    
}

Step 3

create XCTestExpectation variable (in Test_WmBuildGroupsTask):

var theExpectation:XCTestExpectation?

and initiate onDone() method:

func onDone(results: String){
    
    theExpectation?.fulfill() // it will release our "timer"
}

Step 4

Example of our test:

func test___WmBuildGroupsTask() {
   
    // Declare  expectation
     theExpectation = expectationWithDescription("initialized") // dummy text
   
    var task:WmBuildGroupsTask = WmBuildGroupsTask()
    task.delegate = self // pass delegate to WmBuildGroupsTask class        
    task.execute();

    
    // Loop until the expectation is fulfilled in onDone method
    waitForExpectationsWithTimeout(500, { error in XCTAssertNil(error, "Oh, we got timeout")
    })
}// end func

So now what left us to do is to add some stuff to WmBuildGroupsTask:

Step 5

add new variable:

var delegate:MyCallback?

change onPostExecute method to:

func onPostExecute(transferItem:WmTransferItem){
   /* .. */

   delegate?.onDone("finished")// call callback        
}

Thats all.

( Tested )

like image 193
Maxim Shoustin Avatar answered Jan 04 '23 22:01

Maxim Shoustin