今までに遭遇したことがない奇妙な問題に遭遇しています。
ユニットテスト(OCUnitなど)を実行するためにcmd + Uを実行すると、実際にはmain.mが呼び出され、appDelegateを新しく作成して、cmd + Rを押したかのようにアプリを実行しますか?
このDataLayerの背後でCoreDataを使用しているので、質問します。私はテストでDataLayerを正常にモックアウトしていますが、実際にCoreDataを呼び出すgetAllメソッドを実装すると、app/xcodeがマネージオブジェクトモデルに関する例外をスローするため、nilにすることはできません。理解しましたが、実際にはDataLayerクラスを新しくするつもりはありません。mainviewcontrollerloadViewメソッドにブレークポイントを設定し、DataLayer getAllメソッドを呼び出しています。これはモックオブジェクトであるため、テストでは問題になりませんが、実際のインスタンスを呼び出しているようです。
だから私の質問に戻って、cmd + Uを押すと、最初にアプリも実行され、次にテストが実行されますか?
アプリケーションは実際に実行されますが、実行されないようにするために使用できるトリックがあります。
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;
}
これは、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");
}
}
}
これは、標準のプロジェクトレイアウトのSupporting Filesの下にあるmain.mに入ります。
次に、testsディレクトリに以下を追加します。
TestAppDelegate.h
#import <Foundation/Foundation.h>
@interface TestAppDelegate : NSObject<UIApplicationDelegate>
@end
TestAppDelegate.m
#import "TestAppDelegate.h"
@implementation TestAppDelegate
@end
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
}
guard
内のコードは、ストーリーボードから作成されたビューを削除します。
Swiftを使用している場合(おそらくmain.c
がない場合)、次の手順を実行する必要があります。
1:@UIApplicationMain
のAppDelegate.Swift
を削除します
2:空のTestingAppDelegate.Swift
を作成します
import UIKit
class TestingAppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
}
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))
}
私は問題の別の解決策を見つけました:
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, ({
![NSProcessInfo processInfo].environment[@"XCTestConfigurationFilePath"] ?
@"AppDelegate" :
nil;
}));
}
}
ここから: http://qualitycoding.org/app-delegate-for-tests/#comment-63984
はい、テストターゲットにはアプリターゲットへのターゲット依存関係があるため、Cmd + UまたはCmd + Shift + Uを押すと、アプリターゲットがビルドされます。
xCode 7およびxCtoolの使用
xctoolは、アプリを実行せずに単体テストを実行できます。
これを機能させるには、
1。ホストアプリなしで実行するようにターゲット設定を更新します。
プロジェクトを選択->次にテストターゲット->ホストアプリケーションをなしに設定します。
2。 xctoolがない場合はインストールします。
brew install xctool
3。 xctoolでターミナルを使用してテストを実行します。
xctool -workspace yourWorkspace.xcworkspace -scheme yourScheme run-tests -sdk iphonesimulator
すばらしい answers 上記は、実行時にアプリケーションデリゲートを動的に変更することをお勧めします。
私が行う小さな変更は、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);
}
}
@"XCInjectBundleInto"
environmentDictionary
のプロパティは、単体テストバンドルへのパスであり、Xcodeによって設定されます。
私は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
....
}