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@AfterRealm.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もテストも詳しいわけではないので、いろいろ勘違いしている部分があるのかもしれません。

Amazonのほしいものリストを公開しています。仕事で欲しいもの、単なる趣味としてほしいもの、リフレッシュのために欲しいものなどを登録しています。 寄贈いただけると泣いて喜びます。大したお礼はできませんが、よりよい情報発信へのモチベーションに繋がりますので、ご検討いただければ幸いです。