正在调用委托方法的XCTest测试

时间:2022-12-19 20:39:46

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.

我一直在尝试测试我创建的一些使用NSNetServer类执行网络操作的类。我有一些问题确保调用委托方法。

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.

使用[NSThread sleepForTimeInterval:5.0f];和[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0f]];在其他行动发生时简单地暂停。 NSRunLoop方法在第一次调用时工作(如下面的示例代码所示),但在第二次调用时崩溃。我理解也不是“正确”的做事方式,但我不知道“正确”的方式是什么。

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

使用NSCondition和NSConditionLock类,似乎只是锁定代码和回调永远不会被调用。

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

对回调方法中更改的变量使用while循环,与上面相同。

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:

我也试过对NSCondition对象进行锁定:

[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:

使用lock方法时,永远不会调用netServiceBrowser:didFindService:moreComing:方法,当我使用时也是如此:

while (!self.serviceWasFound) {};

while(!self.serviceWasFound){};

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

我还在学习Objective-C,但我完全坚持这个问题。

2 个解决方案

#1


14  

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.

为了处理调用异步执行方法和函数的测试组件,XCTest在Xcode 6中得到了增强,包括使用新API和类XCTestExpectation对象处理块的能力。这些对象响应新的XCTest方法,这些方法允许测试方法等待,直到异步调用返回或达到超时。

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

以下是上述摘录的Apple文档链接。编写异步操作测试

@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");
}

#2


1  

A number of test libraries support testing assynchronous and threaded operations.

许多测试库支持测试异步和线程操作。

They all work essentially the same way:

它们的工作方式基本相同:

  • Wait x seconds for a condition to occur.
  • 等待x秒以发生条件。
  • Optionally perform a set of assertions after the first condition, failing if one of these is not met.
  • 可选地在第一个条件之后执行一组断言,如果不满足其中一个,则失败。
  • Fail if the required condition does not occur within the specified (or default) time.
  • 如果在指定(或默认)时间内未发生所需条件,则失败。

Here are some libraries that provide these features:

以下是一些提供以下功能的库:

  • The expecta matching library.
  • expecta匹配库。
  • The Kiwi test framework.
  • 新西兰测试框架。
  • The Typhoon DI framework has a utility for performing asynchronous integration tests.
  • Typhoon DI框架具有用于执行异步集成测试的实用程序。

As far as I know, all of these libraries use the run-loop approach, as others can result in deadlocks. For this approach to work reliably:

据我所知,所有这些库都使用运行循环方法,因为其他库可能导致死锁。为了使这种方法可靠地工作:

  • When testing block-based callbacks you can create the block inline.
  • 在测试基于块的回调时,您可以创建内联块。
  • For delegate callbacks you should create a separate stub (simplest possible implementation of a protocol) or use a mock ("magic" implementation created using a mocking library), and set this as the delegate. You may have issues if you set the test instance itself as the delegate.
  • 对于委托回调,您应该创建一个单独的存根(最简单的协议实现)或使用mock(使用模拟库创建的“魔术”实现),并将其设置为委托。如果将测试实例本身设置为委托,则可能会出现问题。

Edit:

编辑:

As of Xcode6 there's a new assertion XCTestExpectation for asynchronous testing.

从Xcode6开始,有一个新的断言XCTestExpectation用于异步测试。

Each of the above libraries can be installed using CocoaPods.

可以使用CocoaPods安装上述每个库。

#1


14  

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.

为了处理调用异步执行方法和函数的测试组件,XCTest在Xcode 6中得到了增强,包括使用新API和类XCTestExpectation对象处理块的能力。这些对象响应新的XCTest方法,这些方法允许测试方法等待,直到异步调用返回或达到超时。

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

以下是上述摘录的Apple文档链接。编写异步操作测试

@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");
}

#2


1  

A number of test libraries support testing assynchronous and threaded operations.

许多测试库支持测试异步和线程操作。

They all work essentially the same way:

它们的工作方式基本相同:

  • Wait x seconds for a condition to occur.
  • 等待x秒以发生条件。
  • Optionally perform a set of assertions after the first condition, failing if one of these is not met.
  • 可选地在第一个条件之后执行一组断言,如果不满足其中一个,则失败。
  • Fail if the required condition does not occur within the specified (or default) time.
  • 如果在指定(或默认)时间内未发生所需条件,则失败。

Here are some libraries that provide these features:

以下是一些提供以下功能的库:

  • The expecta matching library.
  • expecta匹配库。
  • The Kiwi test framework.
  • 新西兰测试框架。
  • The Typhoon DI framework has a utility for performing asynchronous integration tests.
  • Typhoon DI框架具有用于执行异步集成测试的实用程序。

As far as I know, all of these libraries use the run-loop approach, as others can result in deadlocks. For this approach to work reliably:

据我所知,所有这些库都使用运行循环方法,因为其他库可能导致死锁。为了使这种方法可靠地工作:

  • When testing block-based callbacks you can create the block inline.
  • 在测试基于块的回调时,您可以创建内联块。
  • For delegate callbacks you should create a separate stub (simplest possible implementation of a protocol) or use a mock ("magic" implementation created using a mocking library), and set this as the delegate. You may have issues if you set the test instance itself as the delegate.
  • 对于委托回调,您应该创建一个单独的存根(最简单的协议实现)或使用mock(使用模拟库创建的“魔术”实现),并将其设置为委托。如果将测试实例本身设置为委托,则可能会出现问题。

Edit:

编辑:

As of Xcode6 there's a new assertion XCTestExpectation for asynchronous testing.

从Xcode6开始,有一个新的断言XCTestExpectation用于异步测试。

Each of the above libraries can be installed using CocoaPods.

可以使用CocoaPods安装上述每个库。