Xcode 中的单元测试,它是否运行应用程序?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15714697/
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
Unit Testing in Xcode, does it run the app?
提问by Mark W
I'm running into a strange problem that I haven't run into before.
我遇到了一个我以前没有遇到过的奇怪问题。
When you do cmd+U to run your Unit Tests (OCUnit for example) does it actually call the main.m, new up the appDelegate and run the app as if your had pressed cmd+R?
当您执行 cmd+U 来运行单元测试(例如 OCUnit)时,它是否真的调用了 main.m、新建了 appDelegate 并运行应用程序,就像您按下了 cmd+R 一样?
I only ask because I'm using CoreData behind this DataLayer. I'm mocking out the DataLayer successfully in my tests, but once I implemented a getAll method that is actually calling CoreData, the app/xcode is throwing an exception about the managed object model can't be nil. Which I understand, but I'm not meaning to actually new up the DataLayer class, and I've put a break point in my mainviewcontroller loadView method where it is calling the DataLayer getAll method. It shouldn't matter with tests because this is a mock object, but it's apparently calling the real instance.
我之所以这么问是因为我在这个 DataLayer 后面使用了 CoreData。我在我的测试中成功模拟了 DataLayer,但是一旦我实现了一个实际调用 CoreData 的 getAll 方法,应用程序/xcode 就会抛出一个关于托管对象模型的异常不能为零。我明白,但我并不打算真正更新 DataLayer 类,而且我已经在调用 DataLayer getAll 方法的 mainviewcontroller loadView 方法中放置了一个断点。测试应该无关紧要,因为这是一个模拟对象,但它显然是在调用真实实例。
So back to my question, when pressing cmd+U does it also run the app first then run the tests?
所以回到我的问题,当按下 cmd+U 时,它是否也先运行应用程序然后运行测试?
回答by Sulthan
The application is actually run but there is a trick you can use to prevent it from running.
该应用程序实际上已运行,但您可以使用一个技巧来阻止它运行。
int main(int argc, char* argv[]) {
int returnValue;
@autoreleasepool {
BOOL inTests = (NSClassFromString(@"SenTestCase") != nil
|| NSClassFromString(@"XCTest") != nil);
if (inTests) {
//use a special empty delegate when we are inside the tests
returnValue = UIApplicationMain(argc, argv, nil, @"TestsAppDelegate");
}
else {
//use the normal delegate
returnValue = UIApplicationMain(argc, argv, nil, @"AppDelegate");
}
}
return returnValue;
}
回答by dwb
Here's a variation of Sulthan's answer that uses XCTest, which is the default for test classes generated by XCode 5.
这是使用 XCTest 的 Sulthan 答案的变体,这是 XCode 5 生成的测试类的默认设置。
int main(int argc, char * argv[])
{
@autoreleasepool {
BOOL runningTests = NSClassFromString(@"XCTestCase") != nil;
if(!runningTests)
{
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
else
{
return UIApplicationMain(argc, argv, nil, @"TestAppDelegate");
}
}
}
This goes into main.m, which should be under Supporting Filesin a standard project layout.
这进入 main.m,它应该在标准项目布局中的支持文件下。
Then in your tests directory add:
然后在您的测试目录中添加:
TestAppDelegate.h
TestAppDelegate.h
#import <Foundation/Foundation.h>
@interface TestAppDelegate : NSObject<UIApplicationDelegate>
@end
TestAppDelegate.m
TestAppDelegate.m
#import "TestAppDelegate.h"
@implementation TestAppDelegate
@end
回答by Tomasz B?k
In Swift, I prefere to bypass a normal execution path inside application: didFinishLaunchingWithOptions
:
在 Swift 中,我更喜欢绕过内部的正常执行路径application: didFinishLaunchingWithOptions
:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
guard normalExecutionPath() else {
window = nil
return false
}
// regular setup
return true
}
private func normalExecutionPath() -> Bool {
return NSClassFromString("XCTestCase") == nil
}
Code inside guard
will remove any views created from storyboard.
里面的代码guard
将删除从故事板创建的任何视图。
回答by Rémy Virin
If you're using Swift (you probably don't have a main.c
), you have to do these steps :
如果您使用 Swift(您可能没有main.c
),则必须执行以下步骤:
1: remove @UIApplicationMain
in AppDelegate.swift
1:删除@UIApplicationMain
在AppDelegate.swift
2: Create an empty TestingAppDelegate.swift
2:创建一个空的 TestingAppDelegate.swift
import UIKit
class TestingAppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
}
3: Create a file called main.swift
:
3:创建一个名为main.swift
:
import Foundation
import UIKit
let isRunningTests = NSClassFromString("XCTestCase") != nil
if isRunningTests {
UIApplicationMain(C_ARGC, C_ARGV, nil, NSStringFromClass(TestingAppDelegate))
} else {
UIApplicationMain(C_ARGC, C_ARGV, nil, NSStringFromClass(AppDelegate))
}
回答by nepo
I found another solution to the problem:
我找到了另一个解决问题的方法:
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, ({
![NSProcessInfo processInfo].environment[@"XCTestConfigurationFilePath"] ?
@"AppDelegate" :
nil;
}));
}
}
From here: http://qualitycoding.org/app-delegate-for-tests/#comment-63984
从这里:http: //qualitycoding.org/app-delegate-for-tests/#comment-63984
回答by rustylepord
Using xCode 7 and xCtool
使用 xCode 7 和 xCtool
xctoolis capable of executing unit tests without running the app.
xctool能够在不运行应用程序的情况下执行单元测试。
To get this working,
为了让这个工作,
1 . Update target settings to run without a host app.
1 . 更新目标设置以在没有主机应用程序的情况下运行。
Select your project --> then test target --> Set the host application to none.
选择您的项目 --> 然后测试目标 --> 将宿主应用程序设置为无。
2. Install xctool , if you don't have it.
2.安装 xctool ,如果你没有它。
brew install xctool
3. Run the tests using terminal with xctool.
3. 使用带有 xctool 的终端运行测试。
xctool -workspace yourWorkspace.xcworkspace -scheme yourScheme run-tests -sdk iphonesimulator
回答by Benjohn
Excellent answersabove that suggest dynamically changing the application delegate at run time.
上面的优秀答案建议在运行时动态更改应用程序委托。
The small modification I make is to detect a unit test runby querying NSProcessInfo
. The advantage is that you don't need to have a class that can be detected to see if unit tests are running.
我所做的小修改是通过查询来检测单元测试运行NSProcessInfo
。优点是您不需要有一个可以检测到单元测试是否正在运行的类。
int main(int argc, char * argv[])
{
// Put your App delegate class here.
const Class appDelegateClass = [ATAppDelegate class];
NSDictionary *const environmentDictionary =
[[NSProcessInfo processInfo] environment];
const BOOL runningUnitTests =
environmentDictionary[@"XCInjectBundleInto"] != nil;
NSString *delegateName =
runningUnitTests ? nil : NSStringFromClass(appDelegateClass);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, delegateName);
}
}
The @"XCInjectBundleInto"
property in environmentDictionary
is the path to your unit tests bundle and is set up by Xcode.
该@"XCInjectBundleInto"
物业environmentDictionary
是通向你的单元测试包中,由Xcode的建立。
回答by sean woodward
Yes, your test target will have a target dependency to the app target, so the app target will be built when you press Cmd+U or Cmd+Shift+U.
是的,您的测试目标将具有对应用程序目标的目标依赖性,因此当您按 Cmd+U 或 Cmd+Shift+U 时将构建应用程序目标。
回答by Francois Nadeau
回答by estemendoza
I use the approach of Tomasz Bak plus some code of dwb answer and come up with the following:
我使用 Tomasz Bak 的方法加上一些 dwb 答案的代码并得出以下结论:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
BOOL runningTests = NSClassFromString(@"XCTestCase") != nil;
if (runningTests) {
self.window.rootViewController = [UIViewController new];
return true;
}
// Your normal code below this
....
}