简体   繁体   中英

Unit testing Parse framework iOS

I am attempting to write unit tests for an iOS application which makes use of the Parse backend framework, and after much experimentation seem to be failing at writing successful unit tests. I have found a few posts on testing asynchronous code ( Testing asynchronous call in unit test in iOS ) and testing network calls, but am yet to find a way of testing calls to the Parse backend with async callbacks.

To give an example, could anyone advise how I would test the following line of code:

[PFUser saveUser:userModelMock withBlock:^(BOOL success, NSError *error) {

}];

I am using OCMock and the XCTest framework.

Any help would be much appreciated.

* EDIT * This is what I have so far but seems to be failing

- (void)testSaveUser {
    id userModelMock = [OCMockObject niceMockForClass:[UserModel class]];
    id userControllerMock = [OCMockObject niceMockForClass:[self.userController class]];

    [[userModelMock expect] saveInBackgroundWithBlock:[OCMArg any]];
    [[userControllerMock stub] saveUser:userModelMock withBlock:[OCMArg any]];

    [userModelMock verify];
}

If you don't mind to utilize a third party library, you may use the following approach:

#import <XCTest/XCTest.h>
#import <RXPromise/RXPromise.h>
...

@interface PFUserTests : XCTestCase
@end

@implementation PFUserTests

// helper:
- (RXPromise*) saveUser:(User*)user {
    RXPromise* promise = [[RXPromise alloc] init];
    [PFUser saveUser:user withBlock:^(Bool success, NSError*error){
        if (success) {
            [promise fulfillWithValue:@"OK"];
        }
        else {
            [promise rejectWithReason:error];
        }
    }];
    return promise;
}


// tests:

-(void) testSaveUser 
{
    User* user = ... ;
    RXPromise* promise = [self saveUser:user];

    // set a timeout:
    [promise setTimeout:5.0];

    [promise.thenOn(dispatch_get_main_queue(), ^id(id result) {
        XCTAssertTrue([result isEqualToString:@"OK"], @"");
        XCTAssertTrue( ... , @"");
        return nil;
    }, ^id(NSError* error) {
        // this may fail either due to a timeout or when saveUser fails:
        XCTFail(@"Method failed with error: %@", error);
        return nil;
    }) runLoopWait];  // suspend the run loop, until after the promise gets resolved 

}

A few notes:

You can execute the success or failure handler (which are registered for the promise with the thenOn statement) on any queue, just specify the queue. This does not dead lock the main thread, even though the handler gets explicitly executed on the main thread, and the test runs as well on the main thread (thanks to the run loop mechanism).

The method runLoopWait will enter a run loop, and only return once the promise has been resolved. This is both, effective and efficient.

A promise accepts a timeout. If the timeout expires, the promise will be "rejected" (that is resolved) with a corresponding error.

IMO, promises are an invaluable tool for handling common asynchronous programming problems - not just a helper for unit tests. (See also: wiki article Futures and promises .

Please note, I'm the author of the RXPromise library , and thus I'm totally biased ;)

There are other Promise implementations for Objective-C as well.

Turns out it was just due to my lack of unit testing understanding. After some research on mock objects, stubs and the like I came up with:

- (void)testSaveUser {
    id userModelMock = [OCMockObject mockForClass:[UserModel class]];
    id userControllerMock = [OCMockObject partialMockForObject:self.userController];

    [[userModelMock expect] saveInBackgroundWithBlock:[OCMArg any]];
    [userControllerMock saveUser:userModelMock withBlock:[OCMArg any]];

    [userModelMock verify];
}

With the macro WAIT_WHILE(<expression>, <time_limit>) in https://github.com/hfossli/AGAsyncTestHelper/ you can write

- (void)testSaveUser
{
    __block BOOL saved = NO;
    [PFUser saveUser:userModelMock withBlock:^(BOOL success, NSError *error) {
        saved = YES;
    }];
    WAIT_WHILE(!saved, 10.0, @"Failed to save user within 10 seconds timeframe);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM