17
votes

Is there any way to programmatically determine if you are running code in the Test target vs the regular Run target while developing iOS applications?

I have the hack of checking if this variable is nil or not as it is only in my test target but this seems pretty hacky.

[[[NSProcessInfo processInfo] environment] objectForKey:@"XCInjectBundle"]
10

10 Answers

15
votes

None of these answers really helped me. I wanted my app to know when I was running tests for the sole purpose of limiting the logging in my test targets. The tests run faster when I'm not logging a bunch of stuff. So, I did it by adding a custom argument to the test portion of my scheme:

Scheme Screenshot

In my application code, I can now check if I am testing or not:

- (void)logError:(NSError*)error{
    if([[[NSProcessInfo processInfo] arguments] containsObject:@"-FNTesting"])
        return;

    NSLog(@"LOG THE ERROR");
}

Thanks to @cameronspickert for being one of the only places I could actually find how to use the custom argument

http://cameronspickert.com/2014/02/18/custom-launch-arguments-and-environment-variables.html

11
votes

You should define proper value for "Preprocessor Macros" in Target Settings.

At the run time you can check it with ifdef sentence.

4
votes

Tested on Xcode 7.3

Create a category on NSProcessInfo.

@implementation NSProcessInfo (RunningTests)

- (BOOL)ag_isRunningTests {
  return ([self.environment objectForKey:@"XCTestConfigurationFilePath"] != nil);
}

@end
4
votes

Now we can simply check this with one line of code for "UITesting".

[[[NSProcessInfo processInfo] arguments] containsObject:@"-ui_testing"]

-ui_testing would appear only when testing the app.

3
votes

Thank you! It helps. Here's some example on swift:

func isRunningTests() -> Bool {

    var arguments = NSProcessInfo.processInfo().arguments as! [String]
    println("arguments ===\(arguments)")

    let testArgs = arguments.filter({ $0 == "-FNTesting" })
    if !testArgs.isEmpty {
        return true
    }

     return false
}
2
votes

In Xcode 6 you can check for the the value of XPC_SERVICE_NAME in the environment variables to see whether the Simulator is running tests or the app directly.

When running directly the variable will have something like UIKitApplication:com.twitter.FabricSampleApp[0xb9f8]

When running unit tests it will look like this: com.apple.xpc.launchd.oneshot.0x10000008.xctest

+ (BOOL)isRunningUnitTests {
    NSString *XPCServiceName = [NSProcessInfo processInfo].environment[@"XPC_SERVICE_NAME"];
    BOOL isTesting = ([XPCServiceName rangeOfString:@"xctest"].location != NSNotFound);

    return isTesting;
}
1
votes

Thanks @steven-hepting, your answer helped me pointing in the correct direction to solve my issue.

But when using "Host Application" in your unit tests "XPC_SERVICE_NAME" will return the same string as normal app start (obviously). So your check alone doesn't always work. That is why I'm also checking TestBundleLocation in addition. Tested this with Xcode 7.2 (7C68).

+ (BOOL)isRunningUnitTests {
    NSDictionary<NSString *, NSString *> *env = [NSProcessInfo processInfo].environment;

    // Library tests
    NSString *envValue = env[@"XPC_SERVICE_NAME"];
    BOOL isTesting = (envValue && [envValue rangeOfString:@"xctest"].location != NSNotFound);
    if (isTesting) {
        return YES;
    }

    // App tests
    // XPC_SERVICE_NAME will return the same string as normal app start when unit test is executed using "Host Application"
    // --> check for "TestBundleLocation" instead
    envValue = env[@"TestBundleLocation"];
    isTesting = (envValue && [envValue rangeOfString:@"xctest"].location != NSNotFound);
    return isTesting;
}
0
votes

In the project settings, on the Info tab, create a new Configuration (in addition to the default "Debug" and "Release"). Then, you will be able to define different preprocessor macros in the target settings (on the "Build Settings" tab) on a per-configuration basis. XCode already uses that to add "DEBUG=1" for the Debug configuration, which allows you to use "#ifdef DEBUG" in your code. You can add any other macros you like this way, such as "TESTING=1".

0
votes

This works for me

  #if Debug_MyTarget_Shortcut_name
    //My_Target_Codes
  #else
    //My_Other_Target_Codes
  #endif

In the Build setting -> Custom Flags, you should add Your shortcut name for your target

0
votes

Xcode 13. Swift version Just check if environment contains XCTestConfigurationFilePath key

    func isTestRunning() -> Bool { ProcessInfo.processInfo.environment.keys.contains("XCTestConfigurationFilePath")}