ios 如何让应用知道它是否在纯 Swift 项目中运行单元测试?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27500940/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to let the app know if it's running Unit tests in a pure Swift project?
提问by bogen
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 that fetches API data. However, I don't want the app to do this when running its tests.
在 Xcode 6.1 中运行测试时一件烦人的事情是整个应用程序必须运行并启动它的故事板和根视图控制器。在我的应用程序中,它运行一些获取 API 数据的服务器调用。但是,我不希望应用程序在运行测试时执行此操作。
With preprocessor macros gone, what's the best for my project to be aware that it was launched running tests and not an ordinary launch? I run them normally with command+ Uand on a bot.
随着预处理器宏的消失,让我的项目知道它是在运行测试而不是普通启动时启动的最好方法是什么?我用command+U和在机器人上正常运行它们。
Pseudocode:
伪代码:
// Appdelegate.swift
if runningTests() {
return
} else {
// do ordinary api calls
}
采纳答案by Eivind Rannem B?hler
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.
您可以在没有主机应用程序本身的情况下运行测试,而不是检查测试是否正在运行以避免副作用。转到项目设置 -> 选择测试目标 -> 常规 -> 测试 -> 主机应用程序 -> 选择“无”。请记住包含运行测试所需的所有文件,以及通常包含在 Host 应用程序目标中的库。
回答by Michael McGuire
Elvind's answer isn't bad if you want to have what used to be called pure "Logic Tests". If you'd still like to run your containing host application yet conditionally execute or not execute code depending on whether tests are run, you can use the following to detect if a test bundle has been injected:
如果您想拥有曾经被称为纯“逻辑测试”的东西,Elvind 的答案还不错。如果您仍然希望运行包含的主机应用程序,但根据是否运行测试有条件地执行或不执行代码,您可以使用以下内容来检测是否已注入测试包:
if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil {
// Code only executes when tests are running
}
I used a conditional compilation flag as described in this answerso that the runtime cost is only incurred in debug builds:
我使用了这个答案中描述的条件编译标志,以便运行时成本仅在调试版本中产生:
#if DEBUG
if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil {
// Code only executes when tests are running
}
#endif
Edit Swift 3.0
编辑 Swift 3.0
if ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil {
// Code only executes when tests are running
}
回答by Jesse
I use this in application:didFinishLaunchingWithOptions:
我在 application:didFinishLaunchingWithOptions 中使用它:
// Return if this is a unit test
if let _ = NSClassFromString("XCTest") {
return true
}
回答by iCaramba
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
.
所有启动参数都会自动添加到您的NSUserDefaults
.
You can now get the BOOL like:
你现在可以得到 BOOL 像:
BOOL test = [[NSUserDefaults standardUserDefaults] boolForKey:@"isTest"];
回答by idStar
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.
我相信想知道您是否在测试中运行是完全合法的。有很多原因可以解释为什么这会有所帮助。例如,在运行测试时,我提前从 App Delegate 中的 application-did/will-finish-launching 方法返回,使测试开始更快,因为代码与我的单元测试无关。然而,由于许多其他原因,我不能进行纯粹的“逻辑”测试。
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).
我曾经使用上面@Michael McGuire 描述的优秀技术。但是,我注意到在 Xcode 6.4/iOS8.4.1 附近停止工作(也许它早点坏了)。
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.
也就是说,在我的框架的测试目标内运行测试时,我不再看到 XCInjectBundle。也就是说,我在测试框架的测试目标中运行。
So, utilizing the approach @Fogmeister suggests, each of my test schemes now sets an environment variable that I can check for.
因此,利用@Fogmeister 建议的方法,我的每个测试方案现在都设置了一个我可以检查的环境变量。
Then, here's some code I have on a class called APPSTargetConfiguration
that can answer this simple question for me.
然后,这是我在一个名为的类上的一些代码APPSTargetConfiguration
,可以为我回答这个简单的问题。
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.
这种方法的一个警告是,如果您从主应用程序方案运行测试,正如 XCTest 会让您这样做的那样(即,不选择您的测试方案之一),您将不会获得此环境变量集。
回答by neoneye
var isRunningTests: Bool {
return ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil
}
Usage
用法
if isRunningTests {
return "lena.bmp"
}
return "facebook_profile_photo.bmp"
回答by m8labs
Combined approach of @Jessy and @Michael McGuire
@Jessy 和 @Michael McGuire 的组合方法
(As accepted answer will not help you while developing a framework)
(在开发框架时,接受的答案对您没有帮助)
So here is the code:
所以这里是代码:
#if DEBUG
if (NSClassFromString(@"XCTest") == nil) {
// Your code that shouldn't run under tests
}
#else
// unconditional Release version
#endif
回答by JosephH
Here's a way I've been using in Swift 4 / Xcode 9 for our unit tests. It's based on Jesse's answer.
这是我在 Swift 4 / Xcode 9 中用于单元测试的方法。它基于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:
完全阻止故事板被加载并不容易,但是如果您在 didFinishedLaunching 的开头添加它,那么它会让您的开发人员非常清楚发生了什么:
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!)
(您显然不应该在 UI 测试中做这样的事情,因为您确实希望应用程序正常启动!)
回答by Fogmeister
You can pass runtime arguments into the app depending on the scheme here...
您可以根据此处的方案将运行时参数传递到应用程序中...
But I'd question whether or not it is actually needed.
但我会质疑它是否真的需要。
回答by ODB
Some of these approaches don't work with UITests and if you're basically testing with the app code itself (rather than adding specific code into a UITest target).
其中一些方法不适用于 UITests,并且如果您基本上是使用应用程序代码本身进行测试(而不是将特定代码添加到 UITest 目标中)。
I ended up setting an environment variable in the test's setUp method:
我最终在测试的 setUp 方法中设置了一个环境变量:
XCUIApplication *testApp = [[XCUIApplication alloc] init];
// set launch environment variables
NSDictionary *customEnv = [[NSMutableDictionary alloc] init];
[customEnv setValue:@"YES" forKey:@"APPS_IS_RUNNING_TEST"];
testApp.launchEnvironment = customEnv;
[testApp launch];
Note that this is safe for my testing since I don't currently use any other launchEnvironment values; if you do, you would of course want to copy any existing values first.
请注意,这对我的测试是安全的,因为我目前不使用任何其他 launchEnvironment 值;如果这样做,您当然希望先复制任何现有值。
Then in my app code, I look for this environment variable if/when I want to exclude some functionality during a test:
然后在我的应用程序代码中,如果/当我想在测试期间排除某些功能时,我会查找此环境变量:
BOOL testing = false;
...
if (! testing) {
NSDictionary *environment = [[NSProcessInfo processInfo] environment];
NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"];
testing = [isRunningTestsValue isEqualToString:@"YES"];
}
Note - thanks for RishiG's comment that gave me this idea; I just expanded that to an example.
注意 - 感谢 RishiG 给我这个想法的评论;我只是将其扩展为一个示例。