Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XCTest testing for delegate methods being called

I've been trying to test some classes I've created that perform networking actions using the NSNetServer class, among others. I'm having some issues ensuring that the delegate method is called.

I've tried numerous methods, including:

Using [NSThread sleepForTimeInterval:5.0f]; and [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0f]]; to simply pause whilst the other actions are happening. The NSRunLoop method works the first time it is called (as shown in the example code below), but crashes on the second call. I understand neither are the "correct" way of doing things, but I don't know what the "correct" way is.

Using the NSCondition and NSConditionLock classes, which just seems to lock up the code and the callback is never called.

Using a while loop on a variable changed in the callback method, same as above.

Below is the code with a couple of extra comments and some of the tests removed for simplicity:

- (void)testCheckCredentials
{
    [self.server start];
    // Create a client
    self.nsb = [[NSNetServiceBrowser alloc] init];
    // Set the delegate to self
    self.nsb.delegate = self;
    // Search for the server
    [self.nsb searchForServicesOfType:self.protocol inDomain:@""];
    // Wait for the service to be found and resolved
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:self.timeout]];
    XCTAssertTrue(self.serviceWasFound, @"Service was not found");
    // Open the connection to the server
    XCTAssertTrue([self.serverConnection open], @"Connection to server failed to open");
    // Wait for the client to connect
    /* This is where it crashes */
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:self.timeout]];
    XCTAssertTrue(self.clientDidConnect, @"Client did not connect");
    /* Further, more class-specific tests */
}

- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)service moreComing:(BOOL)moreComing
{
    NSLog(@"Found a service: %@ (%@)", service.name, service.domain);
    if ([self.serverName isEqualToString:service.name]) {
        self.serviceWasFound = YES;
    }
}

- (void)clientDidConnect:(RCFClientConnection *)client
{
    XCTAssertNotNil(client, @"Connected client is nil");
    self.clientConnection = client;
    self.clientDidConnect = YES;
}

I've also tried doing a lock on an NSCondition object:

[self.nsb searchForServicesOfType:self.protocol inDomain:@""];
// Wait for the service to be found and resolved
[self.lock lockWhenCondition:1];
XCTAssertTrue(self.serviceWasFound, @"Service was not found");

and

self.serviceWasFound = YES;
[self.lock unlockWithCondition:1]

When using the lock method, the netServiceBrowser:didFindService:moreComing: method is never called, same when I use:

while (!self.serviceWasFound) {};

I'm still learning Objective-C but I'm just totally stuck on this problem.

like image 470
Joseph Duffy Avatar asked May 31 '14 16:05

Joseph Duffy


1 Answers

To handle testing components which call asynchronously executing methods and functions, XCTest has been enhanced in Xcode 6 to include the ability to handle blocks using new API and objects of class XCTestExpectation. These objects respond to new XCTest methods that allow the test method to wait until either the async call returns or a timeout is reached.

Here is the link for apple documentation for the above extract. Writing Tests of Asynchronous Operations

@interface sampleAPITests : XCTestCase<APIRequestClassDelegate>{
APIRequestClass *apiRequester;
XCTestExpectation *serverRespondExpectation;
}
@end

//implementation test class
- (void)testAPIConnectivity {
// This is an example of a functional test case.
serverRespondExpectation = [self expectationWithDescription:@"server responded"];
[apiRequester sendAPIRequestForMethod:nil withParams:nil];//send request to server to get tap info
apiRequester.delegate = self;
[self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
    if (error) {
        NSLog(@"Server Timeout Error: %@", error);
    }
   nslog(@"execute here after delegate called  or timeout");
}];
XCTAssert(YES, @"Pass");
}

//Delegate implementation
- (void) request:(APIRequest *)request didReceiveResponse:(NSDictionary *)jsonResponse success:(BOOL)success{
[serverRespondExpectation fulfill];
XCTAssertNotNil(jsonResponse,@"json object returned from server is nil");
}
like image 60
jkr Avatar answered Oct 24 '22 06:10

jkr