1

在单元测试用例中,我尝试为我的应用程序创建一些自己的逻辑,例如从当前的另一个用户名注册或连接到不同的服务器,而无需重复现有代码。我尝试做的第一步是创建 XCTestCase、alloc 类,它支持外部连接并尝试从他那里获得委托调用。但是 NSURLConnection 只是等待没有网络交换,所以我没有成功。

下一个案例,我尝试做的是等待应用程序完成并执行所有问题:

- (BOOL)waitForCompletion:(NSTimeInterval)timeInterval {

NSDate *timeoutDate =
[NSDate dateWithTimeIntervalSinceNow:timeInterval];

do {

    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                             beforeDate:timeoutDate];

    if([timeoutDate timeIntervalSinceNow] < 0.0) {

        break;
    }

} while (self.hasOperationFinishedPerforming == NO);

return self.hasOperationFinishedPerforming;
}

但在这种情况下,应用程序启动,执行所有当前周期,但我不知道如何从当前应用程序代码中检测到正在运行的单元测试。我试图在单元测试目标上设置预处理器宏,但它不会影响应用程序内的代码,也只会影响测试类的代码。

有任何想法吗?

4

2 回答 2

2

假设您已将单元测试目标的 Bundle Loader 构建设置设置为使用您的应用程序,您可以确定您的应用程序是否作为单元测试的一部分运行,如下所示:

静态 BOOL isRunningTests(void)
{
    NSDictionary* 环境 = [[NSProcessInfo processInfo] 环境];
    NSString* injectBundle = environment[@"XCInjectBundle"];
    返回 [[injectBundle pathExtension] isEqualToString:@"octest"]; // 对于 SenTestKit; 对 XCTest 使用“xctest”
}

- (BOOL)应用程序:(UIApplication *)应用程序 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    if (isRunningTests())
        返回是;

    // 这里是正常的应用程序启动内容...
    ...

}

话虽如此,您的单元测试不应该测试任何依赖于可用的外部服务(如 API 或数据库)的东西。如果您无法避免使用这些类型的东西,那么您应该查看“模拟”对象(使用像OCMock这样的 3rd 方库),它可以让您存根复杂的类并控制它们在测试环境中返回的内容。

单元测试的目的不是测试别人的 API 或数据库或 SDK,而是测试你的代码和算法。如果您不能以这种方式测试您的代码,那可能是您需要将代码重构为更可测试的块的信号。

Graham Lee 写了一本名为“测试驱动的 iOS 开发”的好书,我强烈推荐这本书讨论了为 iOS 项目编写单元测试的所有内容。

于 2013-09-26T19:58:24.717 回答
0

当您尝试测试NSURLConnection实现委托的 a 时,您需要注意正确实现与运行循环相关的代码。

您的waitForCompletion:方法可能不起作用或不可靠。原因是,直到在模式下执行“事件”或超时到期后,方法runMode:beforeDate:才会返回。:NSDefaultRunLoopMode一个事件源自某个“运行循环源”,最终成为一个被调用的方法。这个NSURLConnection例子就是这样一个“运行循环源”,因为它调度委托方法,这些委托方法是“事件”,最终由指定模式的目标运行循环执行。

现在,您循环直到self.hasOperationFinishedPerforming == NO. 该标志可能最终被设置,您可能期望运行循环将返回并检查该标志。但是,直到某些任意事件被安排到具有指定模式的运行循环之后,情况才会如此。只有这样(除非过期),runMode:beforeDate:才返回并最终检查标志。

为了使运行循环立即返回,您可以在您的完成处理程序中发送一个虚拟事件,或者在发出异步完成信号时发送一个适当的位置:

    self.hasOperationFinishedPerforming == YES;
    [@"" performSelector:@selector(self) onThread:thread 
              withObject:nil 
           waitUntilDone:NO];

然而,这确实需要完成处理程序知道它将在测试环境中执行。

有一些解决方案可以完全避免这种情况,但需要第三方库。

于 2013-09-26T19:56:06.980 回答