如何让应用程序知道它的运行unit testing纯Swift项目?

在XCode 6.1中运行testing时一件令人讨厌的事情是,整个应用程序必须运行并启动其storyboard和root viewController。 在我的应用程序中运行一些服务器调用来获取API数据。 但是,我不希望应用程序在运行testing时执行此操作。

预处理器的macros不见了,对于我的项目来说,最好的办法是启动运行testing,而不是普通的启动。 我用CMD + U和机器人正常运行它们。

伪代码将是:

// Appdelegate.swift if runningTests() { return } else { // do ordinary api calls } 

而不是检查testing是否运行以避免副作用,您可以运行没有主机应用程序本身的testing。 进入项目设置 – >selecttesting目标 – >常规 – >testing – >主机应用程序 – >select“无”。 请记住包含运行testing所需的所有文件,以及Host应用程序目标通常包含的库。

在这里输入图像说明

如果你想拥有以前被称为纯粹的“逻辑testing”,Elvind的答案并不坏。 如果您仍想运行包含宿主应用程序,但是有条件地执行或不执行代码(取决于是否运行testing),可以使用以下命令来检测是否已经注入了一个testing包:

 if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running } 

我使用了本答案中描述的条件编译标志,以便运行时成本仅在debugging版本中产生:

 #if DEBUG if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running } #endif 

编辑Swift 3.0

 if ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running } 

我在应用程序中使用这个:didFinishLaunchingWithOptions:

 // Return if this is a unit test if let _ = NSClassFromString("XCTest") { return true } 

我相信这是完全合法的,想知道你是否在考试中运行。 有很多原因可以为您提供帮助。 例如,在运行testing时,我会从应用程序委托中的应用程序完成/将要完成启动的方法中尽早返回,从而使testing对于与我的unit testing无密切关系的代码启动得更快。 然而,由于许多其他原因,我不能进行纯粹的“逻辑”testing。

我曾经使用@Michael McGuire所描述的优秀技术。 不过,我注意到,在Xcode 6.4 / iOS8.4.1上停止工作(也许它早就破产了)。

也就是说,在我的框架的testing目标中运行testing时,我再也看不到XCInjectBundle了。 也就是说,我正在testing一个框架的testing目标内部运行。

所以,利用@Fogmeister的build议,我的每个testingscheme现在都设置了一个可以检查的环境variables。

在这里输入图像说明

然后,这里有一些代码我有一个名为APPSTargetConfiguration类可以回答这个简单的问题。

 static NSNumber *__isRunningTests; + (BOOL)isRunningTests; { if (!__isRunningTests) { NSDictionary *environment = [[NSProcessInfo processInfo] environment]; NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"]; __isRunningTests = @([isRunningTestsValue isEqualToString:@"YES"]); } return [__isRunningTests boolValue]; } 

这种方法的一个警告是,如果您从主应用程序scheme运行一个testing,就像XCTest允许您那样做(即不select您的testingscheme之一),您将不会获得此环境variables集。

其他,在我看来更简单的方式:

您可以编辑您的scheme,将布尔值作为启动parameter passing给您的应用程序。 喜欢这个:

在Xcode中设置启动参数

所有启动参数都会自动添加到您的NSUserDefaults

你现在可以得到BOOL像:

 BOOL test = [[NSUserDefaults standardUserDefaults] boolForKey:@"isTest"]; 

您可以将运行时parameter passing到应用程序,具体取决于这里的scheme…

在这里输入图像说明

但我会质疑是否真的需要。

 var isRunningTests: Bool { return ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil } 

用法

 if isRunningTests { return "lena.bmp" } return "facebook_profile_photo.bmp" 

@Jessy和@Michael McGuire的组合方法

(接受的答案在开发框架时不会帮助你)

所以这里是代码:

 #if DEBUG if (NSClassFromString(@"XCTest") == nil) { // Your code that shouldn't run under tests } #else // unconditional Release version #endif 

这是我在Swift 4 / Xcode 9中用于unit testing的一种方法。 这是基于Jesse的回答 。

要防止storyboard被加载并不容易,但是如果你在didFinishedLaunching的开头添加了这个,那么这对开发人员来说是非常清楚的:

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { #if DEBUG if let _ = NSClassFromString("XCTest") { // If we're running tests, don't launch the main storyboard as // it's confusing if that is running fetching content whilst the // tests are also doing so. let viewController = UIViewController() let label = UILabel() label.text = "Running tests..." label.frame = viewController.view.frame label.textAlignment = .center label.textColor = .white viewController.view.addSubview(label) self.window!.rootViewController = viewController return true } #endif 

(你显然不应该为UItesting做任何事情,你希望应用程序正常启动!)