CoreDataのマイグレーションテスト

CoreDataを使う場合、Model(テーブル定義みたいなもの)を変更すると、 マイグレーションが走る。
例えば新しく追加されたカラムについてデフォルト値をどうするかなど。

それをGUIで設定できる反面、各モデルバージョンからのマージ内容を毎回作成しないといけないので、 必ず設定し忘れがある。

マージポリシーなど個別に実装すればよいのだが、ひとまずテストを書いて、 設定漏れがないようにする。

sqlitenの作成

dataに各モデルバージョンのsqliteを置いておく

Data1.sqlite (Version1)
Data2.sqlite (Version2)

CoreDataManager

CoreDataを扱うクラス

@interface CoreDataManager : NSObject
{
    NSManagedObjectModel *managedObjectModel;       
    NSPersistentStoreCoordinator *persistentStoreCoordinator;
}

@property (nonatomic, strong, readonly) NSManagedObjectModel   *managedObjectModel;
@property (nonatomic, strong, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

- (NSString*)storeDirectory; // 保存用ディレクトリ

Test

  1. テスト用の保存ディレクトリ作成
  2. テスト用のSQLのコピー
  3. マイグレーションテスト実行
  4. テスト用保存ディレクトリの削除 ※GHUnit + OCmockを使っています

テスト用の保存ディレクトリ作成を行う

テスト実行前にディレクトリの作成を行う

- (NSString*)testStoreDirectory
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
    return [basePath stringByAppendingPathComponent:@"core_data"];
}

- (void)setUpClass {
    // Run at start of all tests in the class

    // Store用ディレクトリ作成

    NSFileManager* fileManager = [NSFileManager defaultManager];

    NSString* storeDirectory = [self testStoreDirectory];
    if (![fileManager fileExistsAtPath:storeDirectory]) {
        NSError*    error;
        BOOL ret = [fileManager createDirectoryAtPath:storeDirectory
           withIntermediateDirectories:YES attributes:nil error:&error];
        GHAssertTrue(ret, @"create save dir");
    }
}

テスト用SQLコピー

ファイル名をもらってコピーするmethodを作成

- (void)copySQLiteFile:(NSString*)filename
{
    NSString* filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:filename];

    NSString* storePath = [[self testStoreDirectory] stringByAppendingPathComponent:@"Data.sqlite"];

    NSFileManager* fileManager = [NSFileManager defaultManager];

    if ([fileManager fileExistsAtPath:storePath]) {
        [fileManager removeItemAtPath:storePath error:NULL];
    }

    NSError* error;
    BOOL ret = [fileManager copyItemAtPath:filePath toPath:storePath error:&error];
    if (!ret)
        GHTestLog(@"error:%@", [error localizedDescription]);
    GHAssertTrue(ret, @"copy file");
}

マイグレーションテスト実行

OCMockをつかって保存用ディレクトリを書き換えておく

- (void)testDoMigration
    NSArray* tests
    = @[
        @"Data1.sqlite",
        @"Data2.sqlite",
        ];

    for (NSString* sqliteFile in tests) {
        GHTestLog(@"sqlite file :%@", sqliteFile);

        id mock = [OCMockObject partialMockForObject:[[CoreDataManager alloc] init]];
        [[[mock stub] andReturn:[self testStoreDirectory]] storeDirectory];

        [self copySQLiteFile:sqliteFile];

        GHAssertNoThrow([mock doMigration], @"no throw");

        // マイグレーション後のデータチェックとか
    }
}

テスト用保存ディレクトリの削除

- (void)tearDownClass {
    // Run at end of all tests in the class

    // Store用ディレクトリ削除

    NSFileManager* fileManager = [NSFileManager defaultManager];

    NSString* storeDirectory = [self testStoreDirectory];
    if ([fileManager fileExistsAtPath:storeDirectory]) {
        NSError*    error;
        BOOL ret = [fileManager removeItemAtPath:storeDirectory error:&error];
        GHAssertTrue(ret, @"remove save dir");
    }
}

0 件のコメント:

コメントを投稿

ReactNativeでAndroid対応する話

前提 ReactNativeでiOS版のアプリをリリースしていて、Android版をリリースする話 トラブルシューティング Build.VERSION_CODES.Q が存在しないエラー compileSdkVersionを29以上にすると解決 メモリー足りないエラー Execu...