How to let the app know if it's running Unit tests in a pure Swift project?

后端 未结 15 679
借酒劲吻你
借酒劲吻你 2021-01-30 05:08

One annoying thing when running tests in Xcode 6.1 is that the entire app has to run and launch its storyboard and root view controller. In my app this runs some server calls th

相关标签:
15条回答
  • 2021-01-30 05:29

    Other, in my opinion simpler way:

    You edit your scheme to pass a boolean value as launch argument to your app. Like this:

    All launch arguments are automatically added to your NSUserDefaults.

    You can now get the BOOL like:

    BOOL test = [[NSUserDefaults standardUserDefaults] boolForKey:@"isTest"];
    
    0 讨论(0)
  • 2021-01-30 05:29

    Here's a way I've been using in Swift 4 / Xcode 9 for our unit tests. It's based on Jesse's answer.

    It's not easy to prevent the storyboard being loaded at all, but if you add this at the beginning of didFinishedLaunching then it makes it very clear to your developers what is going on:

    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
    

    (you obviously shouldn't do anything like this for UI tests where you do want the app to startup as normal!)

    0 讨论(0)
  • 2021-01-30 05:32

    The method I had been using stopped working in Xcode 12 beta 1. After trying all of the build based answers to this question, I was inspired by @ODB's answer. Here is a Swift version of a fairly simple solution that works for both Real Devices and Simulators. It should also be fairly "release proof".

    Insert in Test setup:

    let app = XCUIApplication()
    app.launchEnvironment.updateValue("YES", forKey: "UITesting")
    app.launch()
    

    Insert in App:

    let isTesting: Bool = (ProcessInfo.processInfo.environment["UITesting"] == "YES")
    

    To use it:

        if isTesting {
            // Only if testing
        } else {
            // Only if not testing
        }
    
    0 讨论(0)
  • 2021-01-30 05:33

    This is the swift way to do it.

    extension Thread {
      var isRunningXCTest: Bool {
        for key in self.threadDictionary.allKeys {
          guard let keyAsString = key as? String else {
            continue
          }
    
          if keyAsString.split(separator: ".").contains("xctest") {
            return true
          }
        }
        return false
      }
    }
    

    And this is how you use it:

    if Thread.current.isRunningXCTest {
      // test code goes here
    } else {
      // other code goes here
    }
    
    

    Here is the full article: https://medium.com/@theinkedengineer/check-if-app-is-running-unit-tests-the-swift-way-b51fbfd07989

    0 讨论(0)
  • 2021-01-30 05:37

    Instead of checking if the tests are running to avoid side-effects, you could run the tests without the host app itself. Go to Project Settings -> select the test target -> General -> Testing -> Host Application -> select 'None'. Just remember to include all files you need to run the tests, as well as libraries normally included by the Host app target.

    enter image description here

    0 讨论(0)
  • 2021-01-30 05:37

    I believe it's completely legitimate to want to know if you're running inside a test or not. There are numerous reasons why that can be helpful. For example, in running tests, I return early from application-did/will-finish-launching methods in the App Delegate, making the tests start faster for code not germane to my unit test. Yet, I can't go pure "logic" test, for a host of other reasons.

    I used to use the excellent technique described by @Michael McGuire above. However, I noticed that stopped working for me around Xcode 6.4/iOS8.4.1 (perhaps it broke sooner).

    Namely, I don't see the XCInjectBundle anymore when running a test inside a test target for a framework of mine. That is, I'm running inside a test target that tests a framework.

    So, utilizing the approach @Fogmeister suggests, each of my test schemes now sets an environment variable that I can check for.

    Then, here's some code I have on a class called APPSTargetConfiguration that can answer this simple question for me.

    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];
    }
    

    The one caveat with this approach is that if you run a test from your main app scheme, as XCTest will let you do, (that is, not selecting one of your test schemes), you won't get this environment variable set.

    0 讨论(0)
提交回复
热议问题