今回はXCTestにおけるRealmの扱い方について、書いていきたいと思います
現状私が知る限りでは2パターンあって、それぞれの特徴を説明して、それを踏まえどちらがおすすめかという流れで進めていこうと思います。
方法1: インメモリオプションを使用する
前提として、テスト実行時に毎回実稼働しているRealmに保存されるのは避けたいです。なのでベタにやるのであれば、テスト終了後に毎回データを削除しにいく手もありますが、それはかなり手間です。そんな時に便利なのがインメモリというオプションで、このオプションを付与したRealmインスタンスでデータを保存すると、データが永続化されずインスタンス解放時には自然と消えてくれます。使い方は下記の通りです。
var config = Realm.Configuration(schemaVersion: SchemaVersion.schemaVersion.rawValue)
config.inMemoryIdentifier = "inMemory"
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm(configuration: config)
方法2: テスト用のRealm保存ファイルを用意する
通常Realmにデータを保存すると、default.realmというファイルに保存されます。テストの際はここに保存するのではなく、そもそも保存するファイルを分けようという考え方です。使い方は下記の通りです。
var config = Realm.Configuration(schemaVersion: SchemaVersion.schemaVersion.rawValue)
let documentDirPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let path = documentDirPath[0] + "/test.realm"
let url = URL(fileURLWithPath: path)
config.fileURL = url
Realm.Configuration.defaultConfiguration = config
realm = try! Realm(configuration: config)
RealmファイルはDocumentsディレクトリ以下に作成されるので、そこにtest.realmというファイルを作ってこのRealmインスタンスを通して保存されたデータはtest.realmに保存されます。
どちらを使うべきか?
テストのケースにもよりますが、「方法2」を個人的にはおすすめします。
理由としては、主に下記の2点からです。
① Realmインスタンスをテスト対象のファイルに渡す必要がない
インメモリオプションを使用する場合、そのオプションをつけた同一のRealmインスタンスをテストファイル側とテスト対象のファイル側双方で使い回す必要があります。双方で別々のインスタンスを立てて、インメモリオプションをつけてデータを保存してもあくまで保存されるのはそれぞれのインスタンス内に一時的である為、テスト対象のファイル側でRealmにデータを保存してもテストファイル側でそれをアサートすることはできないのです。なのでインメモリオプションを使用する場合は、テストファイル側でオプションをつけたRealmインスタンスを作成して、テスト対象のファイルに渡す必要があります。方法2であれば別々のインスタンスで保存しようと、保存先は作成した共通のファイルになるので、テストファイル側も気にせずそのファイルからアサートを書くことができます。
② スレッド跨ぎで更新を行う場合、インメモリオプションでは対応できない
上記の続きでじゃあテストファイルから渡すようにすれば良いじゃんと思うかもしれませんが、下記のようなケースの場合それも難しくなります。
let realm = "テストファイル側から渡されたメインスレッドで生成されたインスタンス"
DispatchQueue.global().async {
realm.write {} // Realm accessed from incorrect thread.
}
Realmインスタンスは生成時のスレッドと更新する際のスレッドを一致させる必要があります。上記の場合はメインスレッドで生成したインスタンスをバックグラウンドスレッドで使用してエラーになっている為、エラーを回避する為には再度バックグラウンドスレッド内で新しくインスタンスを生成させる必要があります。
ただしその場合、上記で説明した通り同じインスタンス内で保存する必要があるので、インメモリオプションを使うことは難しくなります。(※もし回避方法があれば教えてください!)
上記の理由から方法2の方が何かと悩まずに、使いやすいかなと思います
終わりに
以上がXCTestにおけるRealmの取り扱い方です。XCTestを書くにあたってRealmの扱い方をどうしようかと一通り試行錯誤した結果上記の結論に至りました。ぜひXCTestを書く際の参考にして頂ければと思います。この他にも最適な方法がありましたら、ぜひ共有して頂けるとありがたいです!