【问题标题】:What is the proper way to detect if unit tests are running at runtime in Xcode?检测单元测试是否在 Xcode 运行时运行的正确方法是什么?
【发布时间】:2014-09-01 12:50:19
【问题描述】:

当我运行单元测试时,我想跳过一些代码(例如,我不希望 [[UIApplication sharedApplication] openURL:..] 运行)。我正在寻找运行时检查我当前是否正在运行单元测试。

我知道我已经看到了检查 Objective-C 运行时的代码,如果单元测试正在运行,但无法再找到它。

【问题讨论】:

  • 检查this
  • 这就是 mock 的用途。

标签: objective-c ocunit xctest


【解决方案1】:

而不是“我在测试吗?”在整个生产代码中使用条件,我将检查隔离到一个地方:main。在那里,我检查了 alternate 应用程序委托以进行测试。如果它可用,我会使用它而不是常规的应用程序委托。这完全绕过了常规的启动顺序:

int main(int argc, char *argv[])
{
    @autoreleasepool {
        Class appDelegateClass = NSClassFromString(@"TestingAppDelegate");
        if (!appDelegateClass)
            appDelegateClass = [AppDelegate class];
        return UIApplicationMain(argc, argv, nil, NSStringFromClass(appDelegateClass));
    }
}

您可以在此处阅读有关此技术的更多信息:How to Easily Switch Your iOS App Delegate for Testing

【讨论】:

    【解决方案2】:

    我不确定这会持续多久,但它现在对我来说适用于版本 9.0 beta 6 (9M214v)。

    let isTesting = { () -> Bool in
        if let _ = ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] {
            return true
        } else if let testingEnv = ProcessInfo.processInfo.environment["DYLD_INSERT_LIBRARIES"] {
            return testingEnv.contains("libXCTTargetBootstrapInject.dylib")
        } else {
            return false
        }
    }()
    

    无需更改构建环境或方案。

    根据您是运行单个测试用例还是整个测试套件,似乎有两个不同的环境变量在起作用。此外,变量值也会因您是在模拟器中运行还是在真实设备上运行而有所不同。

    【讨论】:

    • 仅供参考,这不再适用于 Xcode 版本 12.0 beta 1 (12A6159)。
    【解决方案3】:

    就用这个吧:

    + (BOOL)isUnitTestRunning
    {
        Class testProbeClass;
        testProbeClass = NSClassFromString(@"XCTestProbe");
        return (testProbeClass != nil);
    }
    

    【讨论】:

    • 一些额外的上下文或解释将有助于解释为什么这个答案是最好的解决方案。
    【解决方案4】:

    我认为您可以像这样检查 Xcode 7.3

    -(BOOL) isRunningUnitTests
    {
        NSDictionary* environment = [ [ NSProcessInfo processInfo ] environment ];
        NSString* theTestConfigPath = environment[ @"XCTestConfigurationFilePath" ];
        return theTestConfigPath != nil;
    }
    

    【讨论】:

    • 不...不适用于 xcode 7.3 和 xcode 8
    【解决方案5】:

    最简单的(并且在 Xcode 7 中使用 XCTest!)检查方法是查看匹配 xctest 包的进程信息:

    static BOOL isRunningTests(void)
    {
        NSDictionary* environment = [[NSProcessInfo processInfo] environment];
        NSString* injectBundle = environment[@"XCInjectBundle"];
        return [[injectBundle pathExtension] isEqualToString:@"xctest"];
    }
    

    来源:https://www.objc.io/issues/1-view-controllers/testing-view-controllers/#integration-with-xcode

    【讨论】:

      【解决方案6】:

      你可以从google-toolbox-for-mac使用this method

      // Returns YES if we are currently being unittested.
      + (BOOL)areWeBeingUnitTested {
        BOOL answer = NO;
        Class testProbeClass;
      #if GTM_USING_XCTEST // you may need to change this to reflect which framework are you using
        testProbeClass = NSClassFromString(@"XCTestProbe");
      #else
        testProbeClass = NSClassFromString(@"SenTestProbe");
      #endif
        if (testProbeClass != Nil) {
          // Doing this little dance so we don't actually have to link
          // SenTestingKit in
          SEL selector = NSSelectorFromString(@"isTesting");
          NSMethodSignature *sig = [testProbeClass methodSignatureForSelector:selector];
          NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
          [invocation setSelector:selector];
          [invocation invokeWithTarget:testProbeClass];
          [invocation getReturnValue:&answer];
        }
        return answer;
      }
      

      之所以使用NSClassFromStringNSInvocation,是为了让代码在不链接到xctest 或ocunit 的情况下进行编译

      【讨论】:

      • 也适用于 iOS
      • 我建议使用[testProbeClass valueForKey:@"isTesting"]; 来避免NSInvocation
      【解决方案7】:

      选择项目,然后选择测试目标:

      选择构建设置并选择全部和组合。在搜索框中输入“preproc” - 您在使用预处理器宏。

      在 Debug 配置中添加一个名为 TEST 的宏并将其设置为等于 1

      然后在你的代码中,你可以这样做:

      #ifndef TEST
          [[UIApplication sharedApplication] doEvilThingForTesting];
      #endif 
      

      或者,如果您希望在测试环境中运行代码:

      #ifdef TEST
          [[UIApplication sharedApplication] doAwesomeTestOnlyThing];
      #endif 
      

      这不完全是 runtime,但单元测试器在运行测试 IIRC 之前编译代码,所以它应该是相同的效果 - 您实际上是在运行测试之前修改代码。

      【讨论】:

      • 它可以工作,但也仅限于使用测试编译的代码,例如目标应用程序代码不受影响(即您的应用程序委托未定义宏)。我解决这个问题的方法是复制 DEBUG 配置并在那里设置宏,然后编辑我的方案以在测试时使用这个新配置。
      猜你喜欢
      • 2011-06-19
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-10
      • 1970-01-01
      • 2015-11-05
      • 2011-05-04
      相关资源
      最近更新 更多