领域单元测试 [英] Realm Unit Testing
问题描述
我正在尝试对Realm及其交互进行单元测试,但进展并不顺利.我已经包含了所有依赖项,并不断出现模糊的故障,下面是Helper
类的代码,该类是Realm
的包装.
I am trying to unit test Realm and its interactions but things are not going too well. I have included all dependencies and keep getting vague failures, below is my code for the Helper
class which is a wrapper over Realm
.
问题
-
这是测试Realm的正确方法吗?
Is this the correct way of testing Realm?
我如何测试应用程序沙箱中的数据,只能通过UI/仪器测试来测试这些数据?
How can I test data that is in the app's sandbox, can that data only be tested by UI/Instrumentation tests?
当前(如下),并且在得到"Powermock零参数构造函数不存在"之前,我得到一个错误.
I am getting an error currently (below) and before I was getting a "Powermock zero args constructor doesn't exist"
以下是我用于单元测试的当前代码:
Below is the current code I have for my Unit test:
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21, application = CustomApplicationTest.class)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "javax.crypto.","java.security.*"})
@SuppressStaticInitializationFor("io.realm.internal.Util")
@PrepareForTest({Realm.class, RealmConfiguration.class,
RealmQuery.class, RealmResults.class, RealmCore.class, RealmLog.class})
public class DatabaseHelperTest {
@Rule
public PowerMockRule rule = new PowerMockRule();
private DatabaseHelper dB;
private Realm realmMock;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockStatic(Realm.class);
mockStatic(RealmConfiguration.class);
mockStatic(RealmCore.class);
mock(DatabaseHelper.class);
final Realm mockRealm = PowerMockito.mock(Realm.class);
realmMock = mockRealm;
final RealmConfiguration mockRealmConfig = PowerMockito.mock(RealmConfiguration.class);
doNothing().when(RealmCore.class);
RealmCore.loadLibrary(any(Context.class));
whenNew(RealmConfiguration.class).withAnyArguments().thenReturn(mockRealmConfig);
when(Realm.getInstance(any(RealmConfiguration.class))).thenReturn(mockRealm);
when(Realm.getDefaultInstance()).thenReturn(mockRealm);
when(Realm.getDefaultInstance()).thenReturn(realmMock);
when(realmMock.createObject(Person.class)).thenReturn(new Person());
Person person = new Person();
person.setId("2");
person.setName("Jerry");
person.setAge("25");
Person person2 = new Person();
person.setId("3");
person.setName("Tom");
person.setAge("22");
List<Person> personsList = new ArrayList<>();
personsList.add(person);
personsList.add(person2);
RealmQuery<Person> personRealmQuery = mockRealmQuery();
when(realmMock.where(Person.class)).thenReturn(personRealmQuery);
RealmResults<Person> personRealmResults = mockRealmResults();
when(realmMock.where(Person.class).findAll()).thenReturn(personRealmResults);
when(personRealmResults.iterator()).thenReturn(personsList.iterator());
when(personRealmResults.size()).thenReturn(personsList.size());
when(realmMock.copyFromRealm(personRealmResults)).thenReturn(personsList);
realmMock = mockRealm;
dB = new DatabaseHelper(realmMock);
}
@Test
public void insertingPerson(){
doCallRealMethod().when(realmMock).executeTransaction(any(Realm.Transaction.class));
Person person = mock(Person.class);
when(realmMock.createObject(Person.class)).thenReturn(person);
dB.putPersonData();
verify(realmMock, times(1)).createObject(Person.class);
verify(person, times(1)).setId(anyString());
}
@Test
public void testExistingData(){
List<Person> personList = dB.getPersonList();
//NPE if checking person object properties i.e name, id. Only list size is available why?
Assert.assertEquals(2, personList.size());
}
@SuppressWarnings("unchecked")
private <T extends RealmObject> RealmQuery<T> mockRealmQuery() {
return mock(RealmQuery.class);
}
@SuppressWarnings("unchecked")
private <T extends RealmObject> RealmResults<T> mockRealmResults() {
return mock(RealmResults.class);
}
错误:
org.mockito.exceptions.misusing.NotAMockException:
Argument passed to verify() is of type Realm$$EnhancerByMockitoWithCGLIB$$317bc746 and is not a mock!
Make sure you place the parenthesis correctly!
See the examples of correct verifications:
verify(mock).someMethod();
verify(mock, times(10)).someMethod();
verify(mock, atLeastOnce()).someMethod();
at com.appstronomy.realmunittesting.db.DatabaseHelperTest.insertingPerson(DatabaseHelperTest.java:133)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
推荐答案
这是测试Realm的正确方法吗?
Is this the correct way of testing Realm?
如何进行官方测试.而单元测试相当复杂:
How about following the official tests. While instrumentation tests seem easy, unit test are quite involved:
@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's constructor.
doNothing().when(RealmCore.class);
RealmCore.loadLibrary(any(Context.class));
// TODO: Mock the RealmConfiguration's 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();
}
上级答案
https://medium.com/@q2ad/android-testing-realm -2dc1e1c94ee1 有一个很好的建议:不要模拟Realm,而要使用一个临时实例.带有依赖项注入的原始命题:使用
https://medium.com/@q2ad/android-testing-realm-2dc1e1c94ee1 has a great proposal: do not mock Realm, but use a temporary instance instead. Original proposition with dependency injection: Use
RealmConfiguration testConfig =
new RealmConfiguration.Builder().
inMemory().
name("test-realm").build();
Realm testRealm = Realm.getInstance(testConfig);
Realm.setDefaultConfiguration(testConfig);
而是设置Realm.getDefaultInstance()
返回的Realm
.
如果收到java.lang.IllegalStateException
,请记住事先调用Realm.init(InstrumentationRegistry.getTargetContext())
,并将文件放在android-test
目录中. (即:使用仪器测试,而不是单元测试).
If you receive a java.lang.IllegalStateException
, remember to call Realm.init(InstrumentationRegistry.getTargetContext())
beforehand, and put the files inside the android-test
directory. (that is: use an instrumentation test, not a unit test).
这篇关于领域单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!