読者です 読者をやめる 読者になる 読者になる

Xcodeのユニットテスト(ロジックテストの方)でsqliteを使ったロジックのテストをする

objective-c

Xcodeユニットテストと言った場合、何気に2個のやり方がありますよね。Appleの単体テストのマニュアルにありますがロジックテストとアプリケーションテストの2個。。。
アプリケーションテストは通常のアプリケーションの実行コンテキストでテストを行い、ロジックテストはいわゆるJUnitとかと同じようなユニットテストですね。
で、単純にテストのプロジェクトを作るとアプリケーションテストの方がセットアップされるのでロジックテストをしたい場合は、別途設定が必要なんでその辺はマニュアルを読めば良いのですが、ロジックテストの場合は通常のアプリケーションの実行と違うのでリソースへのアクセス方法を多少変えないといけないんですねー

例えばsqlite3を使う場合はこんな感じでデータベースファイルへのパスを作れるけど、ロジックテストの場合はこれだとダメ。

#define DATABASE_NAME @"foobar.sqlite"
        NSString *bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:DATABASE_NAME];

じゃあ、どうしよう?と言うことでググるstackoverflowに回答が!
mainBundleを使わずに自分自身からNSBundleのオブジェクトを取得し、そこにデータベースファイル名を付ける形。

        NSBundle *bundle = [NSBundle bundleForClass:[self class]];
        NSString *bundlePath = [[[bundle resourcePath] stringByAppendingString:@"/"] stringByAppendingString:DATABASE_NAME];

これでロジックテストでもアプリケーションテストでもどちらでもOKに。
しかし、ロジックテストの場合はこの処理の前にもう一つ処理が必要。このままでテストを実行するとファイルのコピーに失敗する><
ということで、テストコードのsetUp()でファイルのコピー先が無い場合に備えてディレクトリを作成する。

- (void)setUp
{
    [super setUp];
    // Put setup code here. This method is called before the invocation of each test method in the class.
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    NSString *bundlePath = [bundle resourcePath];
    NSLog(@"bundlePath:%@", bundlePath);
    
    if ([fileManager fileExistsAtPath:bundlePath] == NO) {
        NSError *err;
        if ([fileManager createDirectoryAtPath:bundlePath withIntermediateDirectories:NO attributes:Nil error:&err] == NO) {
            NSLog(@"Create directory failed: %@", err.description);
        }
    }
}

これでロジックテストでもsqliteが使える( ´∀`)bグッ!

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)