Xcode:测试与调试预处理器宏

Posted

技术标签:

【中文标题】Xcode:测试与调试预处理器宏【英文标题】:Xcode: TEST vs DEBUG preprocessor macros 【发布时间】:2011-07-19 13:40:59 【问题描述】:

在创建带有单元测试的新项目时,Xcode 将构建配置设置为测试方案的调试(运行方案相同)。

我应该区分运行 (Command-R) 和测试 (Command-U) 方案吗?

即,我是否应该创建一个名为 Test 的新构建配置,向其中添加预处理器宏 TEST=1,并将其用作测试方案的构建配置?或者,我应该将 Run & Test 都保留为 Debug 吗?

我来自 Ruby/Rails 背景,您通常拥有测试、开发和生产环境。在我看来,Debug 就像开发,Release 就像生产,但是我们缺少一个测试,这就是为什么我认为添加测试可能是有意义的。

评论?意见?有什么建议吗?

我特意问这个是因为我想为 Test 编译一些东西:

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

我认为我是否也为调试编译它并不重要。所以,我真的可以这样做:

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

但是,我现在真的只是打算做测试。所以,这就是为什么我认为我应该区分调试和测试,但想知道为什么 Xcode 默认不为你做这件事? Apple 是否认为您不应该区分它们?

【问题讨论】:

这似乎与编程语言Scheme没有任何关系。 (坏标签) @ryanc,是的,我指的是 Xcode 中的方案。 【参考方案1】:

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

目标-c

static BOOL isRunningTests(void)

    NSDictionary* environment = [[NSProcessInfo processInfo] environment];
    return (environment[@"XCTestConfigurationFilePath"] != nil);

斯威夫特

var unitTesting : Bool 

    return ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil

(已针对 Xcode 11 更新)

【讨论】:

博文链接失效 Xcode 7.3.1 中未设置此环境变量 @AyushGoel - 感谢您的提醒。最好的办法是检查环境字典的内容。似乎您可以查找 XCInjectBundleInto 键。我很快就会更新。 用于快速复制粘贴的 Swift 版本:func isRunningTests() -> Bool let env: [String: String] = NSProcessInfo.processInfo().environment return env["XCInjectBundleInto"] != nil 【参考方案2】:

您可以考虑添加新的构建配置。

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

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

单击“+”按钮添加新配置(如果您愿意,可以将您的配置称为“测试”)。

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

搜索“预处理器宏”

在这里,您可以为新的构建配置添加预处理器宏。

只需双击新的“测试”配置,然后添加 TESTING=1。

最后,编辑您的构建方案。为您的方案选择测试选项。应该有一个“构建配置”下拉菜单。选择您的“测试”配置。

【讨论】:

如果您希望预处理器宏不仅在您的测试代码中可用,而且在常规应用程序中也可用,这是迄今为止最好的方法。比使用辅助方法的其他解决方案干净得多。在 Xcode6 中仍然可以完美运行 如果您使用的是 Cocoapods,请确保在创建新的构建配置后运行 pod install【参考方案3】:

我没有创建测试构建配置,而是:

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

    #define TEST 1
    #import <SenTestingKit/SenTestingKit.h>
    #import "CocoaPlant-Prefix.pch"
    

    在 Tests 目标的构建设置的 Prefix Header 字段中输入了它的路径。

    将以下代码添加到我创建的名为 MyAppDefines.h 的文件的顶部,该文件在 MyApp-Prefix.pch 中导入:

    #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
    

这使我可以在我的意思是[NSBundle mainBundle] 的地方使用BUNDLE,并且在我运行测试时也可以使用它。

Tests-Prefix.pch 中导入 SenTestingKit 还加快了 SenTestingKit 框架的编译速度,并允许我从所有测试文件的顶部省略 #import &lt;SenTestingKit/SenTestingKit.h&gt;

【讨论】:

将#define TEST 1 添加到我的单元测试目标预处理器宏中会在执行单元测试代码中设置宏,但不会在正在单元测试的应用程序代码中设置宏。知道我该如何做到这一点吗? 面临与@shawnwall 相同的问题:编译应用程序时,TEST 变量不在编译器的上下文中。它只是在编译测试用例时出现。 你们有没有想过这个问题?遇到同样的问题。 在下面找到这些评论问题的答案:***.com/a/13928458/623999。 在 XCTest 中起作用的另一件事是 [UIApplication sharedApplication] 在测试期间返回 nil。我无法想象另一个为零的情况,所以我以前用它作为测试标志(如果我错了,请评论!)。【参考方案4】:

我决定在代码本身中添加对环境变量的检查,而不是使用 Robert 提出的 isRunningTests() 建议。

    编辑当前方案(Product/Scheme/Edit Scheme)或Command+ 点击测试配置 点击参数 取消选中“使用运行操作的参数和环境变量” 展开环境变量部分并添加值为 YES 的变量 TESTING 将此添加到您的代码中,并在您需要时调用:
 + (BOOL) isTesting
    
        NSDictionary* environment = [[NSProcessInfo processInfo] environment];
        return [environment objectForKey:@"TESTING"] != nil;
    

完成后屏幕应如下所示。

上面的代码在测试模式或应用模式下运行时会找到TESTING环境变量。此代码位于您的应用程序中,而不是单元测试文件中。你可以使用

#ifdef DEBUG
...
#endif

防止代码在生产环境中执行。

【讨论】:

【参考方案5】:

如果您创建一个测试构建配置,然后将目标的“Other Swift Flags”属性设置为“-DTEST”,它将定义一个可以在您的 swift 代码中工作的 TEST 宏。确保您在 App 目标的构建设置中进行了设置,以便您可以在 App 的 Swift 代码中使用它。

然后用这个集合,你可以像这样测试你的代码:

func testMacro() 
   #if !TEST 
       // skipping over this block of code for unit tests
   #endif

【讨论】:

【参考方案6】:

罗伯特在 SWIFT 3.0 中的回答:

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

【讨论】:

【参考方案7】:

我测试了这么久,找到了一个结果:

不仅你将预处理宏添加到你的单元测试目标中(你可以有很多方法 using variables for unit testing only, 并遵循@MattDiPasquale 方法),

但您还必须在测试目标中添加条件编译文件。 我们应该重新编译这个文件,因为你有这个文件的新预处理器宏, 但是当你的预处理器宏没有设置时,这个文件已经内置在应用程序目标中。

希望对您有所帮助。

【讨论】:

因此,换句话说,对于您希望在测试期间定义 TEST 预处理器宏的主目标中的任何文件,将该文件添加到“构建阶段”下的“编译源”列表中在您的测试目标中。【参考方案8】:

为 Xcode10 更新:

static let isRunningUnitTests: Bool = 
    let environment = ProcessInfo().environment
    return (environment["XCTestConfigurationFilePath"] != nil)
()

【讨论】:

【参考方案9】:

查看环境变量以查看单元测试是否正在运行。类似于罗伯特的回答,但我只检查一次以提高性能。

+ (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;

【讨论】:

【参考方案10】:

ios 上,[UIApplication sharedApplication] 将在单元测试运行时返回 nil

【讨论】:

【参考方案11】:

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

+(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;

【讨论】:

以上是关于Xcode:测试与调试预处理器宏的主要内容,如果未能解决你的问题,请参考以下文章

使用预处理器宏进行条件编译 Xcode

仅为某些架构定义预处理器宏

在没有预处理器宏的情况下,有没有办法在 Xcode 项目的项目级别定义实用的方案特定标志

xctest - 预处理器宏

内联函数与预处理器宏

Doxygen C预处理器宏文档样式