以编程方式确定 iOS 项目中的当前目标(运行或测试)
Posted
技术标签:
【中文标题】以编程方式确定 iOS 项目中的当前目标(运行或测试)【英文标题】:Programmatically determine current target (run or test) in iOS project 【发布时间】:2013-01-11 04:31:26 【问题描述】:在开发 ios 应用程序时,有没有办法以编程方式确定您是在 Test 目标中运行代码还是在常规 Run 目标中运行代码?
我有检查这个变量是否为 nil 的技巧,因为它只在我的测试目标中,但这似乎很 hacky。
[[[NSProcessInfo processInfo] environment] objectForKey:@"XCInjectBundle"]
【问题讨论】:
【参考方案1】:这些答案都没有真正帮助我。我希望我的应用程序知道我何时运行测试,其唯一目的是限制我的测试目标中的日志记录。当我不记录一堆东西时,测试运行得更快。所以,我通过在我的方案的测试部分添加一个自定义参数来做到这一点:
在我的应用程序代码中,我现在可以检查我是否正在测试:
- (void)logError:(NSError*)error
if([[[NSProcessInfo processInfo] arguments] containsObject:@"-FNTesting"])
return;
NSLog(@"LOG THE ERROR");
感谢@cameronspickert 是我唯一能真正找到如何使用自定义参数的地方之一
http://cameronspickert.com/2014/02/18/custom-launch-arguments-and-environment-variables.html
【讨论】:
一旦您使用主机应用程序(现在是 Xcode 7 中的默认设置)测试您的代码,接受的答案就不再起作用。单元测试目标中定义的预处理器宏在应用程序代码(如 AppDelegate)中不可见。所以你必须求助于启动参数或环境变量。我个人更喜欢环境变量,因为NSProcessInfo.processInfo().environment
是字典而不是来自arguments
的数组。【参考方案2】:
您应该在目标设置中为“预处理器宏”定义适当的值。
在运行时你可以用ifdef
语句检查。
【讨论】:
我可能在吹毛求疵,但 #ifdef 永远不会在 /runtime/ 处检查任何内容;这是一个预处理器命令。 我同意。但最终当你运行代码时,你已经确定了目标(运行或测试)。因此,它将提供实施时间的灵活性。 这并没有区分我的常规目标和测试目标。我在我的测试构建设置中将 TEST 添加到调试和发布预处理器宏中,并向我的 App Delegate 添加了一个条件:#ifdef DEBUG static NSString * const MyAppName = @"Test"; #else static NSString * const MyAppName = @"Production"; #endif 但它的价值始终是生产。 您应该添加类似#ifdef TEST 的条件。 添加#ifdef TEST 要么总是成功,要么总是失败。在测试目标以某种方式从构建目标继承一些构建设置之间存在一些问题。【参考方案3】:在 Xcode 7.3 上测试
在NSProcessInfo
上创建一个类别。
@implementation NSProcessInfo (RunningTests)
- (BOOL)ag_isRunningTests
return ([self.environment objectForKey:@"XCTestConfigurationFilePath"] != nil);
@end
【讨论】:
【参考方案4】:现在我们可以简单地用一行代码检查“UITesting”。
[[[NSProcessInfo processInfo] arguments] containsObject:@"-ui_testing"]
-ui_testing 只会在测试应用时出现。
【讨论】:
是专门用于 UI 测试的吗?我们有什么单元测试目标吗? 我没有检查过它的单元测试。您可以打印所有参数并查看是否有用于单元测试的内容【参考方案5】:谢谢!它有助于。以下是关于 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
【讨论】:
【参考方案6】:在 Xcode 6 中,您可以检查环境变量中 XPC_SERVICE_NAME
的值,以查看模拟器是在运行测试还是直接运行应用程序。
当直接运行时,变量将有类似UIKitApplication:com.twitter.FabricSampleApp[0xb9f8]
运行单元测试时,它将如下所示: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;
【讨论】:
【参考方案7】:感谢@steven-hepting,您的回答帮助我指出了解决问题的正确方向。
但是在单元测试中使用“主机应用程序”时,“XPC_SERVICE_NAME”将返回与正常应用程序启动相同的字符串(显然)。因此,仅凭您的支票并不总是有效。这就是为什么我还要检查TestBundleLocation
。使用 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;
【讨论】:
【参考方案8】:在项目设置的信息选项卡上,创建一个新配置(除了默认的“调试”和“发布”)。然后,您将能够根据每个配置在目标设置(在“构建设置”选项卡上)中定义不同的预处理器宏。 XCode 已经使用它为调试配置添加“DEBUG=1”,这允许您在代码中使用“#ifdef DEBUG”。 您可以通过这种方式添加您喜欢的任何其他宏,例如“TESTING=1”。
【讨论】:
您的意思是“XCode 已经使用它来添加“DEBUG=1”而不是“...DEFINE=!”,对吧?:)【参考方案9】:这对我有用
#if Debug_MyTarget_Shortcut_name
//My_Target_Codes
#else
//My_Other_Target_Codes
#endif
在构建设置 -> 自定义标志中,您应该为您的目标添加快捷方式名称
【讨论】:
【参考方案10】:Xcode 13. Swift 版本
只需检查 environment
是否包含 XCTestConfigurationFilePath
键
func isTestRunning() -> Bool ProcessInfo.processInfo.environment.keys.contains("XCTestConfigurationFilePath")
【讨论】:
以上是关于以编程方式确定 iOS 项目中的当前目标(运行或测试)的主要内容,如果未能解决你的问题,请参考以下文章
如何以编程方式在 iOS 应用程序中显示目标的版本/内部版本号?
以编程方式访问正在运行的 iOS 应用程序中的内存使用数据?