18

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"]
fzf
  • 931
  • 1
  • 9
  • 19

10 Answers10

15

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

ganta
  • 366
  • 2
  • 9
  • 2
    Accepted answer doesn't work anymore once you test your code using Host Application (which is now the default in Xcode 7). Preprocessor Macros defines in unit test target are not visible in app code (like AppDelegate). So you have to resort to Launch Arguments or Environment Variables. Personally I prefer env vars because `NSProcessInfo.processInfo().environment` is a dictionary instead of array from `arguments`. – Buju Feb 02 '16 at 17:49
11

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

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

Apurv
  • 17,116
  • 8
  • 51
  • 67
  • 10
    I may be nitpicking, but #ifdef will never check anything at /runtime/; it's a preprocessor command. – sapi Jan 11 '13 at 05:16
  • 1
    I agree. But finally when you are running the code, you have decided the target (run or test). So, it will provide implementation time flexibility. – Apurv Jan 11 '13 at 05:40
  • 1
    This is not differentiating between my regular and test targets. I added TEST to the Debug and Release Preprocessor Macros in my Tests Build Settings and added a condititonal to my App Delegate: #ifdef DEBUG static NSString * const MyAppName = @"Test"; #else static NSString * const MyAppName = @"Production"; #endif Yet it always the value is always production. – fzf Jan 11 '13 at 21:34
  • 1
    You should add condition like #ifdef TEST. – Apurv Jan 12 '13 at 05:03
  • Adding #ifdef TEST is either always successful or always fails. There is some issue here between the test target somehow inheriting some build settings from the build target. – fzf Jan 15 '13 at 02:14
4

Tested on Xcode 7.3

Create a category on NSProcessInfo.

@implementation NSProcessInfo (RunningTests)

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

@end
Ayush Goel
  • 3,134
  • 27
  • 36
4

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.

Manoj
  • 1,482
  • 2
  • 20
  • 53
3

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
}
Community
  • 1
  • 1
Svitlana
  • 2,938
  • 1
  • 29
  • 38
2

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;
}
Steven Hepting
  • 12,394
  • 8
  • 40
  • 50
1

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;
}
Buju
  • 1,546
  • 3
  • 16
  • 27
0

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".

Jacek Lampart
  • 1,741
  • 13
  • 25
0

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

Ahmadreza
  • 6,950
  • 5
  • 50
  • 69
0

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

    func isTestRunning() -> Bool { ProcessInfo.processInfo.environment.keys.contains("XCTestConfigurationFilePath")}
Sergey Brazhnik
  • 661
  • 4
  • 13