Xcode:testing与DEBUG预处理器macros

当使用unit testing创​​build一个新项目时,Xcode将testingscheme的构buildconfiguration设置为Debug(与Runscheme相同)。

我应该区分Run(Command-R)和Test(Command-U)scheme吗?

也就是说,我是否应该创build一个名为Test的新的构buildconfiguration,在其中添加一个预处理器macrosTEST = 1,并将其用作Testscheme的构buildconfiguration? 或者,我应该保持运行和testing都debugging?

我来自一个Ruby / Rails的背景,通常有testing,开发和生产环境。 在我看来,debugging就像开发和发布就像生产,但我们错过了一个testing,这就是为什么我认为它可能是有道理的添加testing。

注释? 意见? build议?

我特别要求这个,因为我想编译一些testing用:

#ifdef TEST // Do something when I test. #endif 

我不认为这很重要,如果我也编译这个debugging。 所以,我真的可以这样做:

 #ifdef DEBUG // Do something when I run or test. #endif 

但是,我真的只是打算现在做testing。 所以,这就是为什么我想我应该区分debugging和testing,但我想知道为什么Xcode默认情况下不会这样做? 苹果是否认为你不应该区分它们?

你可能会考虑添加一个新的构buildconfiguration。

在xcode 4中,点击左侧导航器上的项目。

在主窗口中,点击您的项目,然后select“信息”选项卡。

点击“+”button添加一个新的configuration(你可以打电话给你“testing”,如果你喜欢)。

现在,点击您的目标,然后转到构build设置选项卡。

search“预处理器macros”

在这里,你可以添加预处理器macros来为你的新的构buildconfiguration。

只需双击新的“testing”configuration,并添加TESTING = 1。

最后,编辑你的构buildscheme。 为您的schemeselecttesting选项。 应该有一个“构buildconfiguration”下拉菜单。 select你的“testing”configuration。

预处理器macros将不起作用,您需要在运行时检查环境。

 static BOOL isRunningTests(void) { NSDictionary* environment = [[NSProcessInfo processInfo] environment]; return (environment[@"XCInjectBundleInto"] != nil); } 

(更新了Xcode 7.3)

我不是创build一个testing版本configuration,而是:

  1. 创build了一个Tests-Prefix.pch文件:

     #define TEST 1 #import <SenTestingKit/SenTestingKit.h> #import "CocoaPlant-Prefix.pch" 
  2. 在“testing”目标的构build设置的“前缀头”字段中inputpath。

  3. 将以下代码添加到MyApp-Prefix.pch导入的名为MyAppDefines.h的文件的顶部:

     #ifdef TEST #define TEST_CLASS NSClassFromString(@"AppDelegateTests") // any test class #define BUNDLE [NSBundle bundleForClass:TEST_CLASS] #define APP_NAME @"Tests" #else #define BUNDLE [NSBundle mainBundle] #define APP_NAME [[BUNDLE infoDictionary] objectForKey:(NSString *)kCFBundleNameKey] #endif 

这允许我在任何地方使用BUNDLE [NSBundle mainBundle]并且在运行testing时也可以工作。

Tests-Prefix.pch导入SenTestingKit也可以加快SenTestingKit Framework的编译速度,并允许我从所有testing文件的顶部删除#import <SenTestingKit/SenTestingKit.h>

我决定在代码本身中添加一个环境variables检查,而不是使用Robert提出的isRunningTests()build议。

  1. 编辑当前scheme(产品/scheme/编辑scheme)或Command + <
  2. 点击testingconfiguration
  3. 单击参数取消选中“使用运行操作的参数和环境variables”
  4. 展开环境variables部分并添加值为YES的variablesTESTING
  5. 添加到您的代码的地方,只要你需要:
  + (BOOL) isTesting { NSDictionary* environment = [[NSProcessInfo processInfo] environment]; return [environment objectForKey:@"TESTING"] != nil; } 

完成后,屏幕应该看起来像这样。

屏幕应该是这样的

上面的代码将在testing模式或应用程序模式下运行时findTESTING环境variables。 这段代码进入你的应用程序,而不是unit testing文件。 您可以使用

 #ifdef DEBUG ... #endif 

防止代码在生产中执行。

我testing了这么久,结果发现:

不仅可以将preproessormacros添加到您的unit testing目标(您可以有许多方法只使用variables进行unit testing ,并遵循@MattDiPasquale方法),

还必须在testing目标中添加条件complie文件。 我们应该重新编译这个文件,因为这个文件有一个新的预处理器macros,但是当你的预处理器macros没有设置的时候,这个文件已经构build在应用程序目标中。

希望这对你有所帮助。

Robert在SWIFT 3.0中的回答:

 func isRunningTests() -> Bool { let environment = ProcessInfo().environment return (environment["XCInjectBundleInto"] != nil); } 

查看环境variables以查看unit testing是否正在运行。 与罗伯特的答案类似,但我只为performance检查一次。

 + (BOOL)isRunningTests { static BOOL runningTests; static dispatch_once_t onceToken; // Only check once dispatch_once(&onceToken, ^{ NSDictionary* environment = [[NSProcessInfo processInfo] environment]; NSString* injectBundle = environment[@"XCInjectBundle"]; NSString* pathExtension = [injectBundle pathExtension]; runningTests = ([pathExtension isEqualToString:@"octest"] || [pathExtension isEqualToString:@"xctest"]); }); return runningTests; } 

在iOS上,unit testing运行时, [UIApplication sharedApplication]将返回nil

Kev的答案在Xcode 8.3.2上适用于我的修改版本

 +(BOOL)isUnitTest { static BOOL runningTests; static dispatch_once_t onceToken; // Only check once dispatch_once(&onceToken, ^{ NSDictionary* environment = [[NSProcessInfo processInfo] environment]; if (environment[@"XCTestConfigurationFilePath"] != nil && ((NSString *)environment[@"XCTestConfigurationFilePath"]).length > 0) { runningTests = true; } else { runningTests = false; } }); return runningTests; }