web-dev-qa-db-ja.com

Xcodeでの単体テスト、アプリを実行しますか?

今までに遭遇したことがない奇妙な問題に遭遇しています。

ユニットテスト(OCUnitなど)を実行するためにcmd + Uを実行すると、実際にはmain.mが呼び出され、appDelegateを新しく作成して、cmd + Rを押したかのようにアプリを実行しますか?

このDataLayerの背後でCoreDataを使用しているので、質問します。私はテストでDataLayerを正常にモックアウトしていますが、実際にCoreDataを呼び出すgetAllメソッドを実装すると、app/xcodeがマネージオブジェクトモデルに関する例外をスローするため、nilにすることはできません。理解しましたが、実際にはDataLayerクラスを新しくするつもりはありません。mainviewcontrollerloadViewメソッドにブレークポイントを設定し、DataLayer getAllメソッドを呼び出しています。これはモックオブジェクトであるため、テストでは問題になりませんが、実際のインスタンスを呼び出しているようです。

だから私の質問に戻って、cmd + Uを押すと、最初にアプリも実行され、次にテストが実行されますか?

45
Mark W

アプリケーションは実際に実行されますが、実行されないようにするために使用できるトリックがあります。

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;
}
64
Sulthan

これは、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
20
dwb

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内のコードは、ストーリーボードから作成されたビューを削除します。

10
Tomasz Bąk

Swiftを使用している場合(おそらくmain.cがない場合)、次の手順を実行する必要があります。

1:@UIApplicationMainAppDelegate.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))
}
5
Rémy Virin

私は問題の別の解決策を見つけました:

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

2
nepo

はい、テストターゲットにはアプリターゲットへのターゲット依存関係があるため、Cmd + UまたはCmd + Shift + Uを押すと、アプリターゲットがビルドされます。

1
sean woodward

xCode 7およびxCtoolの使用

xctoolは、アプリを実行せずに単体テストを実行できます。

これを機能させるには、

1。ホストアプリなしで実行するようにターゲット設定を更新します。

プロジェクトを選択->次にテストターゲット->ホストアプリケーションをなしに設定します。

enter image description here

2。 xctoolがない場合はインストールします。

brew install xctool

3。 xctoolでターミナルを使用してテストを実行します。

xctool -workspace yourWorkspace.xcworkspace -scheme yourScheme run-tests -sdk iphonesimulator
1
rustylepord

すばらしい 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によって設定されます。

1
Benjohn

私は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
    ....
}
0
estemendoza

これは、テストターゲットでホストアプリケーションを[なし]に設定することで実行できます。

enter image description here

0
Francois Nadeau