Testing Realm Migrations.
Tags: migrationtest, realmmigration, android, test
Published: 2017-10-19 20:05:21

---
title: "Testing Realm Migrations."
published: true
tags: migration-test,realm-migration,android,test
canonical_url: https://dev.to/aniketsmk/testing-realm-migrations-16g
---

### Why test?

Realm migrations are often, left for the end. They can be stressful, and if you’re doing a manual test, it can take really long to set up or even create the conditions to&nbsp;test.

Let’s see how we can take it down from 20 minutes of manual labor, to 20 seconds of automated test.

### How?

The idea is&nbsp;simple,

- Store the older copy of the db that you will&nbsp;migrate.
- In your instrumentation test, copy the saved db to a temp location.
- Set your configuration to the new schema number and get an instance. (this is where it’s most likely to crash on the&nbsp;spot)
- Verify that you got the right structure.

### Let’s begin with an&nbsp;example.

Full Source Code available here [https://github.com/AniketSK/RealmMigrationTestsExample](https://github.com/AniketSK/RealmMigrationTestsExample)

I have this class. Let’s call it version&nbsp;1.

```
public class Dog extends RealmObject {
    String name;
}
```

I want to keep track of my dogs’ age, so I want to change it to this: Which we will make version&nbsp;2.

```
public class Dog extends RealmObject {
    String name;
    int age;
}
```

This is what my configuration looked like for&nbsp;v1.

```
Realm.init(this);
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder()
        .schemaVersion(1)
        .build();
Realm.setDefaultConfiguration(realmConfiguration);
Realm realm = Realm.getDefaultInstance();
realm.close();
```

The only two lines I will change here for v2 will be in the configuration:

```
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder()
        .schemaVersion(2)
        .migration(new MigrationExample())
        .build();
```

Straightforward, I update the schema version and provide a migration class. This looks&nbsp;like:

```
class MigrationExample implements RealmMigration {

    @Override
    public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
        if (oldVersion < 2) {
            updateToVersion2(realm.getSchema());
        }
    }

    private void updateToVersion2(RealmSchema schema) {
        RealmObjectSchema dogSchema = schema.create(Dog.class.getSimpleName()); // Get the schema of the class to modify.
        dogSchema.addField("age", int.class); // Add the new field.
    }
}
```

There’s a problem here i’ve left in, but we’ll discover what it is during the test&nbsp;:) Can you see it&nbsp;already?

If you’re in a rush, here’s the finished tests, be sure to grab the support file from Realm below&nbsp;it.

- [AniketSK/RealmMigrationTestsExample](https://github.com/AniketSK/RealmMigrationTestsExample/blob/master/app/src/androidTest/java/com/aniket/realmmigrationtestexample/MigrationExampleTest.java)
- [realm/realm-java](https://github.com/realm/realm-java/blob/master/realm/realm-library/src/androidTest/java/io/realm/rule/TestRealmConfigurationFactory.java)

**Understand**

Now, that your code is tested and deployed to production you can relax and understand how this&nbsp;works.

Let’s begin with setting up for the&nbsp;test.

- Store the older copy of the db that you will&nbsp;migrate.

For this, you’re best off running your app on an emulator.

_Why?  
Answer: Emulators provide root access by default and it’s the easiest way to get the older copy of the realm. The older copy of realm is by default in the internal storage of your app in the files&nbsp;folder._

Install and run your older version of the app, on the emulator. This will likely initialize your realm and create the realm file in internal storage. Now you will be able to pull&nbsp;it.

Easiest is with a terminal command with&nbsp;adb.

**adb pull /data/data/com.aniket.realmmigrationtestexample/files/default.realm ~/RealmMigrationTest/app/src/androidTest/assets/realmdb\_1.realm**

With this two things are achieved:

1. The realm file for your app is pulled from internal phone (emulator) storage to your computer.
2. It is stored in the assets of the androidTest folder in your app’s project, with a new name! Now it is called realmdb\_1.realm

**Great, we’re done with step&nbsp;1!**

Note: If you needed to do more complex things, such as create the entire state of your app as it would be after a lot of user interaction, it’s easiest to just do that once manually and then pull it. Be sure all the data you wanted is actually written to&nbsp;disk!

**Setting up the&nbsp;test**

- In your instrumentation test, copy the saved db to a temp location.

This line jumps a few steps. How do we copy the file we have in assets to a new realm file that we can&nbsp;use?

Realm already has written a class that helps with this and a lot of support activities required for it. This is the TestRealmConfigurationFactory, which can be taken from their repo from here [https://github.com/realm/realm-java/blob/master/realm/realm-library/src/androidTest/java/io/realm/RealmMigrationTests.java](https://github.com/realm/realm-java/blob/master/realm/realm-library/src/androidTest/java/io/realm/RealmMigrationTests.java)

_Why copy their file? Surely there’s a better way?  
Answer: This is internal to their project and just isn’t exported elsewhere in a manner that I’m aware of. If you do know of a better way to get this into your project without copying it, let me know in the comments._

Ok, now that you have this file, let’s create a bog standard instrumentation test to use&nbsp;it.

```
@RunWith(AndroidJUnit4.class)
public class MigrationExampleTest {

    @Rule
    public final TestRealmConfigurationFactory configFactory = new TestRealmConfigurationFactory();

    @Before
    public void setUp() throws Exception {

    }

    @Test
    public void migrate() throws Exception {

    }

}
```

Your migration test should look like this to start&nbsp;with.

Note, if you don’t have the TestRealmConfigurationFactory copied to your project yet, it will show up as an&nbsp;error.

If you missed it, you’ll need to pick this up from the earlier mentioned location. Be sure to change the package and keep the copyright text!

**Setup Complete!**

**Writing the&nbsp;tests**

Now let’s begin with the tests themselves. Add this to the&nbsp;tests.

```
@Test(expected = RealmMigrationNeededException.class)
public void migrate_migrationNeededIsThrown() throws Exception {
    String REALM_NAME = "realmdb_1.realm";
    RealmConfiguration realmConfig = new RealmConfiguration.Builder()
            .name(REALM_NAME) // Keep using the temp realm.
            .schemaVersion(1) // Original realm version.
            .build();// Get a configuration instance.
    configFactory.copyRealmFromAssets(context, REALM_NAME, realmConfig); // Copy the stored version 1 realm file from assets to a NEW location.
    Realm.getInstance(realmConfig);
}
```

![](https://cdn-images-1.medium.com/max/915/1*OWCHxdjdPI5fsxgOIqyvGQ.png)

The REALM\_NAME, should match the name of the file you pulled from adb. Recall that we renamed it from its default name to realmdb\_1.realm and stored it in the assets folder, for androidTest. Not the assets folder under&nbsp;main!

Next we set the schema version as it was originally, and get a realm&nbsp;config.

Then with the configFactory, copy the realm in the assets, to a new location _separate from the default realm_. This just prevents us from deleting the default realm (since the TestRealmConfigFactory deletes the namedRealm before it’s&nbsp;copied)

Then we try to get an instance of it. Which should fail since the Dog object has already had the age field added to&nbsp;it.

This verifies that a MigrationNeededException is actually&nbsp;thrown.

**Test Part&nbsp;2:**

In this test, we’re going actually do the migration!

Get the realm configuration again, this time incrementing the schema version and adding your main Migration class.

```
@Test
public void migrate_migrationSuceeds() throws Exception {
    String REALM_NAME = "realmdb_1.realm"; // Same name as the file for the old realm which was copied to assets.
    RealmConfiguration realmConfig = new RealmConfiguration.Builder()
            .name(REALM_NAME)
            .schemaVersion(2) // NEW realm version.
            .migration(new MigrationExample())
            .build();// Get a configuration instance.
    configFactory.copyRealmFromAssets(context, REALM_NAME, realmConfig); // Copy the stored version 1 realm file
    // from assets to a NEW location.
    // Note: the old file is always deleted for you.
    // by the copyRealmFromAssets.
    Realm realm = Realm.getInstance(realmConfig);
    assertTrue("The age field was not added.", realm.getSchema().get(Dog.class.getSimpleName())
        .hasField("age"));

    assertEquals(realm.getSchema().get(Dog.class.getSimpleName())
        .getFieldType("age"), RealmFieldType.INTEGER);
    realm.close();
}
```

Once this is done, get the realm instance again. This time the migration will run, and your test should fail!  
Here we finally run into the error I mentioned I’d put in. I originally created the Dog.class all over again before adding the&nbsp;field.

So in the MigrationExample.java change ‘create’:

```
private void updateToVersion2(RealmSchema schema) {
    RealmObjectSchema dogSchema = schema.create(Dog.class.getSimpleName()); // Get the schema of the class to modify.
    dogSchema.addField("age", int.class); // Add the new field.
}
```

To ‘get’:

```
private void updateToVersion2(RealmSchema schema) {
    RealmObjectSchema dogSchema = schema.get(Dog.class.getSimpleName()); // Get the schema of the class to modify.
    dogSchema.addField("age", int.class); // Add the new field.
}
```

Run this again and your test will&nbsp;pass!

**Congratulations, you can now test Realm Migrations!**

### Conclusion:

Testing realm migrations with instrumentation tests is pretty easy and can help you track down tricky bugs. For instance, you can now run realm queries and other operations on the final realm you receive at the end of this test. You can verify for instance, that all your existing dogs, had a default age assigned to them. You could ensure that some data transformation you were doing, completed successfully.

As always, you can set debug pointers and step through the migrations to fix issues you encounter.

I hope this stripped down example will help you to more thoroughly test your application and bring a difficult to set test, into the _realm_ of easy testability.

See more examples of how to use this&nbsp;at:

[realm/realm-java](https://github.com/realm/realm-java/blob/master/realm/realm-library/src/androidTest/java/io/realm/RealmMigrationTests.java)

Until next time, remember, if you’re doing any manual repetitive work, there’s always a better&nbsp;way!

You can reach me on twitter @AniketSMK I’m always happy to get comments and suggestions.

Notes:

If you find emulators difficult to run, or your test db can only be created on a real device, you can always&nbsp;use

```
File externalStorageFile = new File(getExternalFilesDir(null), "copiedInternalRealmToExternal.realm");
realm.writeCopyTo(externalStorageFile);
```

and pick up the file from there. This will copy your internal realm to an external location, where even real devices wouldn’t obstruct you from picking it&nbsp;up.

This was edited on 7th August, 2017 to correct one of the&nbsp;tests.