10

OCUnit を使用してアプリをテストすると、テストを実行する前に、通常どおり AppDelegate、ウィンドウ、および rootViewController がセットアップされます。私の rootViewController は、いくつかの NSNotifications のオブザーバーとして自身を追加します。

分離されたテスト インスタンスとモック オブザーバーを使用してこれらの通知をテストすると、自動的に作成された rootViewController の通知ハンドラーも呼び出され、一部のテストが失敗します。

OCUnit が rootViewController を作成しないようにする方法や、テスト モードで実行しているときに別の ViewController クラスを使用する方法はありますか? アプリのコードに特別なテスト関連のコードを書かずにこれができたら最高です。

4

4 に答える 4

12

更新:今日私がしていることは、以下の答えとは少し異なります。テスト用にアプリデリゲートを簡単に切り替える方法をご覧ください

アプリコードにテスト固有のコードを少し追加する必要があります。完全な起動シーケンスを回避するために私が行うことは次のとおりです。

スキームを編集する

  • テストアクションを選択します
  • [テスト]で[引数]タブを選択します
  • 「実行アクションのオプションを使用する」を無効にする
  • 環境変数を追加し、次のように設定runningTestsしますYES

アプリデリゲートを編集する

  • -application:didFinishLaunchingWithOptions:意味がわかり次第、以下を追加してください。

    #if DEBUG
        if (getenv("runningTests"))
            return YES;
    #endif
    
  • 同じことをし-applicationDidBecomeActive:ますが、単純にreturn

于 2012-08-16T05:31:49.357 に答える
3

@Jon Reidのソリューションは素晴らしく、現在すべてのプロジェクトで使用していますが、小さな問題があります。デフォルトでは、スキームはバージョン管理システムに保持されません。そのため、git からプロジェクトを複製すると、runningTests環境変数が設定されていないという理由だけでテストが失敗する可能性があります。そして、私はいつもそれを忘れています。

念のため、すべてのプロジェクトに小さなテストを追加します。

#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>

@interface DMAUnitTestModeTests : XCTestCase

@end

@implementation DMAUnitTestModeTests

- (void)testUnitTestMode {
    BOOL isInUnitTestMode = (BOOL)getenv("runningTests");

    XCTAssert(isInUnitTestMode, @"You have to set a 'runningTests' environment variable in the schemes editor.");
    //http://stackoverflow.com/questions/11974138/prevent-app-from-creating-a-viewcontroller-when-running-unit-tests/11981192#11981192
}

@end

誰かがより良い解決策を思いついたら、私に知らせてください:)

回答として投稿した理由:これは、@Jon Reid の回答 (私が本当に気に入っている) を少し改善しただけです。コメントとして書きたかったのですが、このようにコードを共有するのは不便なので、回答として投稿することにしました(質問に対する正確な回答ではないにもかかわらず)。

于 2015-06-04T09:47:52.423 に答える
2

テストの実行時に Xcode 自体が環境変数を設定するため、スキームで作成する必要はありません。他の目的ですでにそうしている場合は、そうするのが実用的かもしれません。ただし、テストが実行されているかどうかを判断するために、Xcode の環境変数を使用できます。コードの大部分は objc では次のようになります。これをアプリのデリゲートにスローできます。

オプション1:

static BOOL isRunningTests(void) __attribute__((const));

static BOOL isRunningTests(void)
{
    NSDictionary* environment = [[NSProcessInfo processInfo] environment];
    NSString* injectBundle = environment[@"XCInjectBundle"];
    NSLog(@"TSTL %@", [injectBundle pathExtension]);
    return [[injectBundle pathExtension] isEqualToString:@"xctest"] || [[injectBundle pathExtension] isEqualToString:@"octest"];
}

isRunningTests()次に、テストを確認する必要がある場所に電話するだけです。ただし、このコードは実際には別の場所、たとえば TestHelper クラスに格納する必要があります。

オプション 2:

// TestHelper.h
#import <Foundation/Foundation.h>

extern BOOL isRunningTests(void) __attribute__((const));
// TestHelper.m
#import "TestCase.h"

extern BOOL isRunningTests(void)
{
    NSDictionary* environment = [[NSProcessInfo processInfo] environment];
    NSString* injectBundle = environment[@"XCInjectBundle"];
    NSLog(@"TSTL %@", [injectBundle pathExtension]);
    return [[injectBundle pathExtension] isEqualToString:@"xctest"] || [[injectBundle pathExtension] isEqualToString:@"octest"];
}

まだグローバル変数を使用しており、クラス名の選択は実際には無関係であることに注意してください。それを維持することが理にかなっているクラスです。

オプション 3:

そして、Swift では、Objective-C と Swift の両方で動作するようにクラスでラップする必要があります。次のようにできます。

class TestHelper: NSObject {
    static let isRunningTests: Bool = {
        guard let injectBundle = NSProcessInfo.processInfo().environment["XCInjectBundle"] as NSString? else {
            return false
        }
        let pathExtension = injectBundle.pathExtension

        return pathExtension == "xctest" || pathExtension == "octest"
    }()
}
于 2015-08-22T08:26:44.630 に答える