Bagusflyer
Bagusflyer

Reputation: 12925

How to write XCTestCase for asynchronized method?

I'm working on a unit test for one of my model which is using asynchronized call to my rest api.The method used to request my API is like this:

requestOnComplete:(void(^)())complete onError:(void(^)(NSString* errMsg))fail;

In my test case:

-(void)testMyApiCall
{
    [myObj requestOnComplete:^{

        XCTAssertTrue(YES,@"Success");

    } onError:^(NSString *errorString) {

        XCTFail(@"Failed.%@", errorString);

    }];
}

As I expected, this test always pass because of the asynchronized call. Can anybody advise on this issue? Thanks.

Upvotes: 0

Views: 134

Answers (2)

larva
larva

Reputation: 5148

You can use lib XCAsyncTestCase It simple to do XCTestCas asynchronized method. Ex as your test function here is your code:

-(void)testMyApiCall
{
    [myObj requestOnComplete:^{

        [self notify:XCTestAsyncTestCaseStatusSucceeded];

    } onError:^(NSString *errorString) {

        [self notify:XCTestAsyncTestCaseStatusFailed];

    }];
    [self waitForStatus:XCTestAsyncTestCaseStatusSucceeded timeout:10];
}

Upvotes: 1

Bryan Chen
Bryan Chen

Reputation: 46598

I use these helper functions

BOOL XLCRunloopRunUntil(CFTimeInterval timeout, BOOL (^condition)(void));

#define XLCAssertTrueBeforeTimeout(expr, timeout, format...) \
XCTAssertTrue( (XLCRunloopRunUntil(timeout, ^BOOL{ return expr; })) , ## format )

static inline void XLCRunloopRunOnce()
{
    while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, YES) == kCFRunLoopRunHandledSource ||
           CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, YES) == kCFRunLoopRunHandledSource);
}

static inline void XLCRunloopRun(CFTimeInterval timeout)
{
    CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, NO);
    XLCRunloopRunOnce();
}

BOOL XLCRunloopRunUntil(CFTimeInterval timeout, BOOL (^condition)(void)) {
    static mach_timebase_info_data_t timebaseInfo;
    if ( timebaseInfo.denom == 0 ) {
        mach_timebase_info(&timebaseInfo);
    }

    uint64_t timeoutNano = timeout * 1e9;

    uint64_t start = mach_absolute_time();
    do {
        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.01, YES);
        XLCRunloopRunOnce();
        uint64_t end = mach_absolute_time();
        uint64_t elapsed = end - start;
        uint64_t elapseNano = elapsed * timebaseInfo.numer / timebaseInfo.denom;
        if (elapseNano >= timeoutNano) {
            return NO;
        }
    } while (!condition());

    return YES;
}

example

-(void)testMyApiCall
{
    __block BOOL done = NO;
    [myObj requestOnComplete:^{

        // XCTAssertTrue(YES,@"Success"); // this line is pointless
        done = YES;

    } onError:^(NSString *errorString) {

        XCTFail(@"Failed.%@", errorString);
        done = YES;

    }];

    XLCAssertTrueBeforeTimeout(done, 1, "should finish within 1 seconds");
}

Upvotes: 1

Related Questions