web-dev-qa-db-ja.com

実行中の単体テストが純粋なSwiftプロジェクトであるかどうかをアプリに知らせる方法は?

XCode 6.1でテストを実行する際の厄介な点の1つは、アプリ全体を実行して、ストーリーボードとルートのviewControllerを起動する必要があることです。私のアプリでは、APIデータを取得するサーバー呼び出しをいくつか実行します。ただし、テストを実行するときにアプリにこれを行わせたくありません。

プリプロセッサマクロがなくなったので、通常の起動ではなくテストを実行して起動したことをプロジェクトが認識するのに最適なものは何ですか?通常はCMD + Uを使用してボットで実行します。

擬似コードは次のとおりです。

// Appdelegate.Swift

if runningTests() {
   return
} else {
   // do ordinary api calls
}
67
bogen

副作用を避けるためにテストが実行されているかどうかを確認する代わりに、ホストアプリ自体なしでテストを実行できます。 [プロジェクトの設定]->テスト対象を選択->一般->テスト->ホストアプリケーション-> [なし]を選択します。テストを実行するために必要なすべてのファイルと、通常ホストアプリターゲットによって含まれるライブラリを含めることを忘れないでください。

enter image description here

39

Elvindの答えは、純粋な「論理テスト」と呼ばれていたものを持ちたい場合は悪くありません。テストを実行するかどうかに応じて、条件付きでコードを実行するかどうかを含むホストアプリケーションを実行したい場合は、次を使用してテストバンドルが挿入されたかどうかを検出できます。

if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil {
     // Code only executes when tests are running
}

この回答では のように条件付きコンパイルフラグを使用したため、ランタイムコストはデバッグビルドでのみ発生します。

#if DEBUG
    if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil {
        // Code only executes when tests are running
    }
#endif

編集Swift 3.0

if ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil {
    // Code only executes when tests are running
}
69
Michael McGuire

Application:didFinishLaunchingWithOptionsでこれを使用します:

// Return if this is a unit test
if let _ = NSClassFromString("XCTest") {
    return true
}
36
Jesse

その他、私の意見ではより簡単な方法で:

スキームを編集して、起動引数としてブール値をアプリに渡します。このような:

Set launch arguments in Xcode

すべての起動引数はNSUserDefaultsに自動的に追加されます。

次のようなBOOLを取得できます。

BOOL test = [[NSUserDefaults standardUserDefaults] boolForKey:@"isTest"];
19
iCaramba

テスト内で実行しているかどうかを知りたいのは完全に合法だと思います。これが役立つ理由は数多くあります。たとえば、テストの実行中に、App Delegateのapplication-did/will-finish-launchingメソッドから早く戻り、ユニットテストに関係のないコードのテストをより早く開始します。しかし、他の多くの理由により、純粋な「論理」テストを行うことはできません。

上記の@Michael McGuireによって説明された優れた手法を使用していました。ただし、Xcode 6.4/iOS8.4.1で動作しなくなったことに気付きました(おそらくすぐに壊れてしまったのでしょう)。

つまり、私のフレームワークのテストターゲット内でテストを実行すると、XCInjectBundleが表示されなくなります。つまり、フレームワークをテストするテストターゲット内で実行しています。

したがって、@ Fogmeisterが提案するアプローチを利用して、私のテストスキームのそれぞれが、確認できる環境変数を設定するようになりました。

enter image description here

次に、この簡単な質問に答えられる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];
}

このアプローチの1つの注意点は、XCTestでできるように(つまり、テストスキームの1つを選択しないで)メインアプリスキームからテストを実行する場合、この環境変数は設定されないことです。

17
idStar
var isRunningTests: Bool {
    return ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil
}

使用法

if isRunningTests {
    return "lena.bmp"
}
return "facebook_profile_photo.bmp"
7
neoneye

ユニットテストにSwift 4/Xcode 9で使用している方法です。これは Jesseの答え に基づいています。

ストーリーボードが読み込まれるのを防ぐのは簡単ではありませんが、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

(アプリを通常どおり起動するUIテストでは、明らかにこのようなことはすべきではありません!)

4
JosephH

ここでスキームに応じてアプリにランタイム引数を渡すことができます...

enter image description here

しかし、私はそれが実際に必要かどうか疑問に思うでしょう。

2
Fogmeister

@Jessyと@Michael McGuireの組み合わせアプローチ

(フレームワークの開発中に受け入れられた答えはあなたを助けません)

コードは次のとおりです。

#if DEBUG
        if (NSClassFromString(@"XCTest") == nil) {
            // Your code that shouldn't run under tests
        }
#else
        // unconditional Release version
#endif
1
m8labs

これらのアプローチのいくつかはUITestで機能せず、基本的にアプリコード自体でテストしている場合(特定のコードをUITestターゲットに追加するのではなく)。

私はテストの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];

現在、他のlaunchEnvironment値を使用していないため、これはテストにとって安全です。その場合、もちろん既存の値を最初にコピーする必要があります。

次に、アプリコードで、テスト中に一部の機能を除外するかどうか、またはそのタイミングでこの環境変数を探します。

BOOL testing = false;
...
if (! testing) {
    NSDictionary *environment = [[NSProcessInfo processInfo] environment];
    NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"];
    testing = [isRunningTestsValue isEqualToString:@"YES"];
}

注-このアイデアをくれたRishiGのコメントに感謝します。これを例に拡張しました。

0
ODB