Realmのテストのやり方を知りたい
Realmを使ってみました。ちなみに私は、今まではGreenDAOとAndroid Ormaしか使ったことがありません。
とりあえずCRUD操作のやり方をつかもうとテストを書いてみました。テストの書き方が根本的に間違っている可能性が無きにしもあらずですが、こんな感じで作りました。
public class FilterDataSourceRealmTest {
private static RealmConfiguration config;
private static FilterDataSourceRealm sut;
@BeforeClass
public static void initializeTest() {
config = new RealmConfiguration.Builder()
.name("test_realm")
.deleteRealmIfMigrationNeeded()
.build();
sut = new FilterDataSourceRealm(config);
}
@Before
public void setUp() {
Realm.deleteRealm(config);
}
@After
public void tearDown() {
Realm.deleteRealm(config);
}
@Test
public void insertFilter() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
sut.insertFilter("test.com/");
sut.getFilter("test.com/")
.subscribe(new Action1<UriFilter>() {
@Override
public void call(UriFilter uriFilter) {
assertThat(uriFilter.getFilter(), is("test.com/"));
latch.countDown();
}
});
latch.await(2, TimeUnit.SECONDS);
}
}
テスト対象のコード(一部抜粋)はこんな感じです。
public class FilterDataSourceRealm implements FilterDataSource {
private RealmConfiguration config;
public FilterDataSourceRealm(RealmConfiguration config) {
this.config = config;
}
@Override
public void insertFilter(String insert) {
Realm realm = Realm.getInstance(config);
realm.beginTransaction();
realm.copyToRealmOrUpdate(new UriFilter(insert));
realm.commitTransaction();
realm.close();
}
このテストコードはandroidTestに配置して実機で実行します(Instrumentation Test)。
テストのたびにデータをまっさらにするため、@Before
と@After
でRealm.deleteRealm()
を呼び出しています。
FilterDataSourceRealmはDaggerを使ってシングルトンで運用します。初期化時にRealmConfigrationを与えて、Realmのインスタンスは各メソッドの中でインスタンス生成&closeを行うようになっています。
で、このテストコードでテストを行うと、テストメソッド内でRealm関連の操作が失敗した場合にjava.lang.IllegalStateException: It's not allowed to delete the file associated with an open Realm. Remember to close() all the instances of the Realm before deleting its file: /data/data/jp.gcreate.sample.daggersandbox/files/test_realm
というエラーが出ます。Realmへの操作で何らかのエラーが生じたんでしょうが、出て来るエラーは「close忘れてるぞ」になります。
例外の意味は分かります。Realm.deleteRealm()
を実行するときにcloseしてないRealmのインスタンスが存在してはいけないということです。ですが、この場合のエラーの本質はClose忘れではなく、Realmの操作が失敗していることです。私はその原因が何なのか知りたいわけです。insertFilter
が失敗したからcloseが行われず、その結果@After
で実行しようとしたRealm.deleteRealm()
が失敗しているわけですから。
実装中はこの「close忘れ」エラーが出るたびに、@Before
と@After
の処理をコメントアウトし、本当のエラーの原因を確認していました。すると実際にどこで失敗しているのかエラーメッセージが教えてくれました。
テストメソッドの度にRealmのファイルを消すというのが愚策なんですかねぇ・・・。でも消さないとテストの実行順で登録データが異なることになってテストにならないし。
公式サンプルにPowerMockを使ったテストがありましたが、PowerMockよくわからないのと、Mockじゃなくて実際に書き込みとかしたかったのでこんな形のテストを書いてみたのですが、テスト失敗のログがまったく役に立たなくて苦労しました。
Realmもテストも詳しいわけではないので、いろいろ勘違いしている部分があるのかもしれません。