Realm Unit Test on Android

Attempting to unit test a class that makes some calls in Realm (0.87.4) using

java.lang.NoClassDefFoundError: rx/Observable$OnSubscribe at io.realm.RealmConfiguration$Builder.<init>(RealmConfiguration.java:279) at org.testapp.db.MyClassTest.setUp(MyClassTest.java:34) ... Caused by: java.lang.ClassNotFoundException: rx.Observable$OnSubscribe at java.net.URLClassLoader$1.run(URLClassLoader.java:366) 

My test class starts with:

 @RunWith(MockitoJUnitRunner.class) public class MyClassTest extends TestCase { @Rule public TemporaryFolder testFolder = new TemporaryFolder(); Realm realm; @Before public void setUp() throws Exception { File tempFolder = testFolder.newFolder("realmdata"); RealmConfiguration config = new RealmConfiguration.Builder(tempFolder).build(); realm = Realm.getInstance(config); } ... 

My gradle has:

 testCompile 'junit:junit:4.12' testCompile "org.mockito:mockito-core:1.10.19" testCompile "org.robolectric:robolectric:3.0" compile 'io.realm:realm-android:0.87.4' 

How to solve this?

=== EDIT 1 ===

I added to my gradle:

 testCompile 'io.reactivex:rxjava:1.1.0' 

and

 android { // ... testOptions { unitTests.returnDefaultValues = true } } 

new mistake

 java.lang.UnsatisfiedLinkError: no realm-jni in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1886) at java.lang.Runtime.loadLibrary0(Runtime.java:849) at java.lang.System.loadLibrary(System.java:1088) at io.realm.internal.RealmCore.loadLibrary(RealmCore.java:117) 
+5
source share
3 answers

Unit tests are difficult or impossible when using Realm in the class you are testing (thanks to Dmitry for mentioning). What I can do is run the tests as instrumental tests (thanks Dmitry, Christian).

And it's pretty simple, I donโ€™t need to change anything for the testing methods ...

a. Move the test class to the "androidTest" folder instead of "test". (Starting with Android Studio 1.1, you should put your unit tests in / src / test and Android Instrumentation Tests in / src / androidTest)

B. Add dependencies for instrumental tests in the gradle assembly file, use "androidTest" because they are instrumental:

 androidTestCompile 'junit:junit:4.12' androidTestCompile 'io.reactivex:rxjava:1.1.0' androidTestCompile 'com.android.support.test:runner:0.4.1' androidTestCompile 'com.android.support.test:rules:0.4.1' androidTestCompile 'com.android.support:support-annotations:23.1.1' 

C. In the test class, replace the runner at the top with AndroidJUnit4:

 @RunWith(AndroidJUnit4.class) public class MyClassTest extends TestCase { ... 

Create an Android launch configuration like โ€œAndroid Testsโ€, launch it and voila, it will check the same methods now, but on the device. I am very happy.

+7
source

Since Realm 0.87 , you also need to include RxJava in your dependencies:

 compile 'io.reactivex:rxjava:1.1.0' 
+2
source

Official tests show how to do this. While tool tests seem easy (as you found out), unit tests are quite in demand :

 @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21) @PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"}) @SuppressStaticInitializationFor("io.realm.internal.Util") @PrepareForTest({Realm.class, RealmConfiguration.class, RealmQuery.class, RealmResults.class, RealmCore.class, RealmLog.class}) public class ExampleActivityTest { // Robolectric, Using Power Mock https://github.com/robolectric/robolectric/wiki/Using-PowerMock @Rule public PowerMockRule rule = new PowerMockRule(); private Realm mockRealm; private RealmResults<Person> people; @Before public void setup() throws Exception { // Setup Realm to be mocked. The order of these matters mockStatic(RealmCore.class); mockStatic(RealmLog.class); mockStatic(Realm.class); mockStatic(RealmConfiguration.class); Realm.init(RuntimeEnvironment.application); // Create the mock final Realm mockRealm = mock(Realm.class); final RealmConfiguration mockRealmConfig = mock(RealmConfiguration.class); // TODO: Better solution would be just mock the RealmConfiguration.Builder class. But it seems there is some // problems for powermock to mock it (static inner class). We just mock the RealmCore.loadLibrary(Context) which // will be called by RealmConfiguration.Builder constructor. doNothing().when(RealmCore.class); RealmCore.loadLibrary(any(Context.class)); // TODO: Mock the RealmConfiguration constructor. If the RealmConfiguration.Builder.build can be mocked, this // is not necessary anymore. whenNew(RealmConfiguration.class).withAnyArguments().thenReturn(mockRealmConfig); // Anytime getInstance is called with any configuration, then return the mockRealm when(Realm.getDefaultInstance()).thenReturn(mockRealm); // Anytime we ask Realm to create a Person, return a new instance. when(mockRealm.createObject(Person.class)).thenReturn(new Person()); // Set up some naive stubs Person p1 = new Person(); p1.setAge(14); p1.setName("John Young"); Person p2 = new Person(); p2.setAge(89); p2.setName("John Senior"); Person p3 = new Person(); p3.setAge(27); p3.setName("Jane"); Person p4 = new Person(); p4.setAge(42); p4.setName("Robert"); List<Person> personList = Arrays.asList(p1, p2, p3, p4); // Create a mock RealmQuery RealmQuery<Person> personQuery = mockRealmQuery(); // When the RealmQuery performs findFirst, return the first record in the list. when(personQuery.findFirst()).thenReturn(personList.get(0)); // When the where clause is called on the Realm, return the mock query. when(mockRealm.where(Person.class)).thenReturn(personQuery); // When the RealmQuery is filtered on any string and any integer, return the person query when(personQuery.equalTo(anyString(), anyInt())).thenReturn(personQuery); // RealmResults is final, must mock static and also place this in the PrepareForTest annotation array. mockStatic(RealmResults.class); // Create a mock RealmResults RealmResults<Person> people = mockRealmResults(); // When we ask Realm for all of the Person instances, return the mock RealmResults when(mockRealm.where(Person.class).findAll()).thenReturn(people); // When a between query is performed with any string as the field and any int as the // value, then return the personQuery itself when(personQuery.between(anyString(), anyInt(), anyInt())).thenReturn(personQuery); // When a beginsWith clause is performed with any string field and any string value // return the same person query when(personQuery.beginsWith(anyString(), anyString())).thenReturn(personQuery); // When we ask the RealmQuery for all of the Person objects, return the mock RealmResults when(personQuery.findAll()).thenReturn(people); // The for(...) loop in Java needs an iterator, so we're giving it one that has items, // since the mock RealmResults does not provide an implementation. Therefore, anytime // anyone asks for the RealmResults Iterator, give them a functioning iterator from the // ArrayList of Persons we created above. This will allow the loop to execute. when(people.iterator()).thenReturn(personList.iterator()); // Return the size of the mock list. when(people.size()).thenReturn(personList.size()); this.mockRealm = mockRealm; this.people = people; } @Test public void shouldBeAbleToAccessActivityAndVerifyRealmInteractions() { doCallRealMethod().when(mockRealm).executeTransaction(Mockito.any(Realm.Transaction.class)); // Create activity ExampleActivity activity = Robolectric.buildActivity(ExampleActivity.class).create().start().resume().visible().get(); assertThat(activity.getTitle().toString(), is("Unit Test Example")); // Verify that two Realm.getInstance() calls took place. verifyStatic(times(2)); Realm.getDefaultInstance(); // verify that we have four begin and commit transaction calls // Do not verify partial mock invocation count: https://github.com/jayway/powermock/issues/649 //verify(mockRealm, times(4)).executeTransaction(Mockito.any(Realm.Transaction.class)); // Click the clean up button activity.findViewById(R.id.clean_up).performClick(); // Verify that begin and commit transaction were called (been called a total of 5 times now) // Do not verify partial mock invocation count: https://github.com/jayway/powermock/issues/649 //verify(mockRealm, times(5)).executeTransaction(Mockito.any(Realm.Transaction.class)); // Verify that we queried for Person instances five times in this run (2 in basicCrud(), // 2 in complexQuery() and 1 in the button click) verify(mockRealm, times(5)).where(Person.class); // Verify that the delete method was called. Delete is also called in the start of the // activity to ensure we start with a clean db. verify(mockRealm, times(2)).delete(Person.class); // Call the destroy method so we can verify that the .close() method was called (below) activity.onDestroy(); // Verify that the realm got closed 2 separate times. Once in the AsyncTask, once // in onDestroy verify(mockRealm, times(2)).close(); } 
0
source

Source: https://habr.com/ru/post/1242089/


All Articles