3

Core Data アプリを単体テストしたい (多数のレコードを使用したスト​​レス テスト)。ユニットとアプリケーションのテスト用にすべてがセットアップされ、正常に動作しています。

多くのコア データ オブジェクトを作成し、グラフ ビュー コントローラーがまだ機能するかどうかを確認したいと考えています。どうすればいいですか?

MyAppApplicationTest.mテストクラスでテストメソッドを作成すると、テスト後にアプリが終了するだけで、グラフビューコントローラーとやり取りする方法がありません。

で多くのレコードを作成し、AppDelegate後でそのコードを削除する必要がありますか? または、単体テスト フレームワークを使用する方法はありますか?

ご協力いただきありがとうございます。

4

1 に答える 1

1

UI テストにはいくつかのオプションがあります。ただし、この場合、巨大なデータベースを構築し、さまざまなテスト用に保持することをお勧めします。オプションで、コマンドライン、環境、または単にユーザーのデフォルトで値を設定することで使用できます。

ユーザーのデフォルトを確認するためのサンプル コード、次に設定のための環境...

static NSString * findOption(NSString *name) {
    NSString *result = nil;
    NSDictionary *options  = [NSUserDefaults standardUserDefaults];
    if ((result = [options objectForKey:name]) != nil) return result;
    options = [[NSProcessInfo processInfo] environment];
    if ((result = [options objectForKey:name]) != nil) return result;
    return nil;
}

ユーザーデフォルトのすべてのドメインではなく、コマンドラインパラメーターのみを確認したい場合は、これを使用できます...

NSDictionary *options  = [[NSUserDefaults standardUserDefaults] volatileDomainForName:NSArgumentDomain];

次に、永続ストアを作成するコードで、オプションが設定されているかどうかを確認できます...

if ((value = findOption(@"MundiLargeData")) && value.boolValue) {
    // Create the persistent store with the pre-generated big database
    // If creation failed, can continue with normal database as failsafe
}

また、テストに SenTest を使用する場合は、コマンド ライン パラメーターを使用することに注意してください。

NSString *value = findOption(@"SenTest");
if (value) {
    NSLog(@"Using SenTest: %@", value);
}

コードをそのままにしておくことも、#ifdef を除外することもできます。そこに置いておくだけでかなり安全です。

編集

お詫び -- すぐに追加するつもりだったのに、呼ばれてしまった…

申し訳ありません。テスト コードを出荷することを意味するつもりはありません。あなたは確かにそれをしたくありません。アプリケーションを実行しているときに、別のバージョンをコンパイルすることなくデバイスで手動の UI テストを実行できるように、大規模なデータベースをロードする方法を探しているだけだと思いました。

そのようなことをしたい場合は、多くのオプションがあります。テストするクラスのカテゴリとしてテストを記述し、そのファイルをリリース ビルドから除外するだけです。「test」または「runtimeTest」の接頭辞のように、テストに一貫した命名スキームを与える場合、次のようなメソッドを持つことができます...

- (void)runAllMethodsThatBeginWith:(NSString*)prefix {
    Class aClass = [self class];
    Method *methods;
    unsigned methodCount;
    if ((methods = class_copyMethodList(aClass, &methodCount)))
    {
        // For this example, we only want methods that take no arguments and return void
        char const *desiredEncoding = method_getTypeEncoding(class_getClassMethod([NSObject class], @selector(load)));

        for (unsigned i = 0; i < methodCount; ++i) {
            SEL selector = method_getName(methods[i]);
            NSString *name = NSStringFromSelector(selector);
            char const * typeEncoding = method_getTypeEncoding(methods[i]);
            NSLog(@"%@: %s %s", name, typeEncoding, desiredEncoding);
            NSRange range = [name rangeOfString:prefix];
            if (range.location == 0 && range.length == prefix.length && strcmp(desiredEncoding, typeEncoding) == 0) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                [self performSelector:selector];
#pragma clang diagnostic pop
            }
        }

        // Don't forget to free the allocated methods array
        free(methods);
    }
}

何らかの名前で始まり、void を返し、引数を取らないクラス内のすべてのメソッドが検索されます。他の引数処理を行うこともできますが、その場合は ARC 関連の問題に対処する必要があります (コンパイラは何をすべきかわからないため、少なくとも警告が表示されます)。とにかく、それはあなたが始めるためのものです...型エンコーディングをパラメーターとして追加して、より一般的にすることができます...

これで、ランタイム コードで、次のように呼び出すことができます...

[self runAllMethodsThatBeginWith:@"runtimeTest"];

次のようなすべてのメソッドを実行します...

- (void)runtimeTestFoo {
}

何もない場合は、黙って何もしません。

これらの実装を含むファイル全体をリリース ビルドから除外するか、マクロ ifdef で除外することができます。

現在、テストはリリースにコンパイルされていませんが、他のもののためのものであり、必要なときにいつでもテストを呼び出すことができます。特定のテストがわかっている場合は、もちろん、respondsToSelector: を使用して、その特定のテスト メソッドを条件付きで実行できます。

編集

うーん。何をすべきかを動的に決定する方法を探していると思いました。それだけで十分な場合は、データベースを作成する AppDelegate のサブクラスを提供するだけです...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Create your mondo database
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

ここで、いくつかのオプションがあります。main.o では、使用するアプリ デリゲート クラスを指定します。オプション (#ifdef DEBUG)、環境変数、またはその他の手段を使用して、使用するアプリ デリゲート クラスを指定できます...

#import "AppDelegate.h"
#define APP_DELEGATE AppDelegate

#ifdef USE_MY_SPECIAL_RUNTIME_TEST_DELEGATE
#import "RuntimeTestDelegate.h"
#undef APP_DELEGATE
#define APP_DELEGATE RuntimeTestDelegate
#endif

int main(int argc, char *argv[])
{
    @autoreleasepool
    {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([APP_DELEGATE class]));
    }
}

または、単に NSClassFromString(@"MyTestingAppDelegate") を呼び出して、リンクされているかどうかを確認することもできます...

または、完全に分離したい場合は、単に別のターゲットを作成します。そこにアプリデリゲートサブクラスを配置し、そのターゲットの main.m で使用します。他のすべてのファイルに対してリンクします。

これで、アプリを起動する前にデータベースを構築する特別なアプリ デリゲートがあることを除いて、「プロダクション」と同じ、完全に別の実行可能ファイルができました。

テストは難しいです。自分が何を望んでいて、何を望んでいないかを正確に知る必要があります。すべての状況を網羅する正解はありません。

これには他にも多くのオプションがあります。たとえば、リソース バンドルに構成ファイルを提供する、アプリの plist に余分なものを含める、実行中に特別なコマンドを送信できる「グル」モードをアプリに提供するなどです (ソケットを開き、特別なコマンドを読み取り、応答を送り返す - これにより、必要なシナリオをスクリプト化して、Mac で実行し、アプリをリモートで制御できます - このためのツールもあります)。

うまくいけば、これらの方法のいずれかがあなたが探しているものに合うでしょう。

于 2012-08-02T21:13:00.970 に答える