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.
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");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With