如何使用 Mockito 在 android 中正确测试 ORM?
How to correctly test ORM in android using Mockito?
我是 Android 的 Mockito 测试新手,在理解如何测试数据源方法时遇到问题。我使用在应用程序 class 中初始化的 RushOrm:
AndroidInitializeConfig config = new AndroidInitializeConfig(getApplicationContext());
List<Class<? extends Rush>> classes = new ArrayList<>();
classes.add(CardCollection.class);
classes.add(Note.class);
config.setClasses(classes);
RushCore.initialize(config);
我的数据源class是这样的:
public class CollectionsRepository implements CollectionDataSource {
private static CollectionsRepository INSTANCE = null;
private final CollectionDataSource dataSource;
// Prevent direct instantiation.
private CollectionsRepository(@NonNull CollectionDataSource dataSource) {
this.dataSource = checkNotNull(dataSource);
}
public static CollectionsRepository getInstance(CollectionDataSource dataSource) {
if (INSTANCE == null) {
INSTANCE = new CollectionsRepository(dataSource);
}
return INSTANCE;
}
@Override
public void getCollections(@NonNull LoadCollectionsCallback callback) {
dataSource.getCollections(callback);
}
@Override
public void getCollection(@NonNull String collectionId, @NonNull GetCollectionCallback callback) {
dataSource.getCollection(collectionId, callback);
}
@Override
public void saveCollection(@NonNull CardCollection cardCollection, @NonNull final SaveOrUpdateCollectionCallback callback) {
dataSource.saveCollection(cardCollection, callback);
}
@Override
public void updateCollection(@NonNull CardCollection cardCollection, @NonNull SaveOrUpdateCollectionCallback callback) {
dataSource.updateCollection(cardCollection, callback);
}
@Override
public void deleteCollection(@NonNull CardCollection cardCollection) {
dataSource.deleteCollection(cardCollection);
}
@Override
public void deleteAllCollections(@NonNull RushCallback callback) {
dataSource.deleteAllCollections(callback);
}
}
这是我对存储库 class 的测试 class:
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP, application = ProjectApplication.class)
@RunWith(MockitoJUnitRunner.class)
public class CollectionsRepositoryTest {
private CollectionPresenter presenter;
@Mock
private CollectionsRepository repository;
@Mock
private CollectionContract.View view;
@Mock
private CollectionDataSourceImplementation dataSource;
@Mock
private CollectionDataSource.GetCollectionCallback getCollectionCallback;
@Mock
private CollectionDataSource.LoadCollectionsCallback loadCollectionsCallback;
@Mock
private CollectionDataSource.SaveOrUpdateCollectionCallback saveOrUpdateCollectionCallback;
@Captor
private ArgumentCaptor<CollectionDataSource.GetCollectionCallback> getCollectionCallbackArgumentCaptor;
@Captor
private ArgumentCaptor<CollectionDataSource.LoadCollectionsCallback> loadCollectionsCallbackArgumentCaptor;
@Captor
private ArgumentCaptor<CollectionDataSource.SaveOrUpdateCollectionCallback> saveOrUpdateCollectionCallbackArgumentCaptor;
private static List<CardCollection> collections = Lists.newArrayList(new CardCollection("Title1", "Description1", null),
new CardCollection("Title2", "Description2", null));
private final static String collection_title = "title";
@Before
public void setUp() {
// Mockito has a very convenient way to inject mocks by using the @Mock annotation. To
// inject the mocks in the test the initMocks method needs to be called.
MockitoAnnotations.initMocks(this);
// Get a reference to the class under test
presenter = new CollectionPresenter(repository, view);
}
@Test
public void getCollections() {
dataSource.getCollections(loadCollectionsCallback);
verify(dataSource).getCollections(loadCollectionsCallbackArgumentCaptor.capture());
loadCollectionsCallbackArgumentCaptor.getValue().onSuccess(collections);
}
@Test
public void getCollection() {
dataSource.getCollection(collection_title, getCollectionCallback);
verify(dataSource).getCollection(eq(collection_title), any(CollectionDataSourceImplementation.GetCollectionCallback.class));
}
@Test
public void saveCollection() {
CardCollection collection = new CardCollection("Title", "Description", null);
dataSource.saveCollection(collection, saveOrUpdateCollectionCallback);
verify(dataSource).saveCollection(collection, saveOrUpdateCollectionCallbackArgumentCaptor.capture());
saveOrUpdateCollectionCallbackArgumentCaptor.getValue().onSuccess(collection);
}
@Test
public void updateCollection() {
}
@Test
public void deleteCollection() {
}
@Test
public void deleteAllCollections() {
}
}
saveCollection 方法失败。我正在谷歌搜索,主要是寻找如何测试直接 sqlite 连接的方法。任何想法都会有所帮助。谢谢。
我认为错误可能出在这一行:
verify(dataSource).saveCollection(collection, saveOrUpdateCollectionCallbackArgumentCaptor.capture());
更改方式:
verify(dataSource).saveCollection(collection, saveOrUpdateCollectionCallback);
您无法验证具有不同参数的方法。
看来您的单元测试有误:
你的方法是做什么的:
- 测试方法在mocked对象
上调用一个方法
- 然后你 "verify" 调用了模拟方法
简单地说:这没有意义。这个想法是将 mocked 对象传递给 production 代码;然后你触发对生产代码的某种操作。最后,您验证那些您期望看到的调用确实发生了。
除此之外:您不会测试 库 做了它应该做的事情(好吧,至少在单元测试中没有)。
相反,您的单元测试应该只测试您的产品所做的布线。换句话说:您唯一需要测试的是您的生产代码传递给某些库方法调用的参数是否符合预期。
当然,您应该做一些 "integration level" 测试以确保整个过程正常 end-to-end。但这通常 超出 单元测试的范围(正如名称已经告诉我们的那样!)
我是 Android 的 Mockito 测试新手,在理解如何测试数据源方法时遇到问题。我使用在应用程序 class 中初始化的 RushOrm:
AndroidInitializeConfig config = new AndroidInitializeConfig(getApplicationContext());
List<Class<? extends Rush>> classes = new ArrayList<>();
classes.add(CardCollection.class);
classes.add(Note.class);
config.setClasses(classes);
RushCore.initialize(config);
我的数据源class是这样的:
public class CollectionsRepository implements CollectionDataSource {
private static CollectionsRepository INSTANCE = null;
private final CollectionDataSource dataSource;
// Prevent direct instantiation.
private CollectionsRepository(@NonNull CollectionDataSource dataSource) {
this.dataSource = checkNotNull(dataSource);
}
public static CollectionsRepository getInstance(CollectionDataSource dataSource) {
if (INSTANCE == null) {
INSTANCE = new CollectionsRepository(dataSource);
}
return INSTANCE;
}
@Override
public void getCollections(@NonNull LoadCollectionsCallback callback) {
dataSource.getCollections(callback);
}
@Override
public void getCollection(@NonNull String collectionId, @NonNull GetCollectionCallback callback) {
dataSource.getCollection(collectionId, callback);
}
@Override
public void saveCollection(@NonNull CardCollection cardCollection, @NonNull final SaveOrUpdateCollectionCallback callback) {
dataSource.saveCollection(cardCollection, callback);
}
@Override
public void updateCollection(@NonNull CardCollection cardCollection, @NonNull SaveOrUpdateCollectionCallback callback) {
dataSource.updateCollection(cardCollection, callback);
}
@Override
public void deleteCollection(@NonNull CardCollection cardCollection) {
dataSource.deleteCollection(cardCollection);
}
@Override
public void deleteAllCollections(@NonNull RushCallback callback) {
dataSource.deleteAllCollections(callback);
}
}
这是我对存储库 class 的测试 class:
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP, application = ProjectApplication.class)
@RunWith(MockitoJUnitRunner.class)
public class CollectionsRepositoryTest {
private CollectionPresenter presenter;
@Mock
private CollectionsRepository repository;
@Mock
private CollectionContract.View view;
@Mock
private CollectionDataSourceImplementation dataSource;
@Mock
private CollectionDataSource.GetCollectionCallback getCollectionCallback;
@Mock
private CollectionDataSource.LoadCollectionsCallback loadCollectionsCallback;
@Mock
private CollectionDataSource.SaveOrUpdateCollectionCallback saveOrUpdateCollectionCallback;
@Captor
private ArgumentCaptor<CollectionDataSource.GetCollectionCallback> getCollectionCallbackArgumentCaptor;
@Captor
private ArgumentCaptor<CollectionDataSource.LoadCollectionsCallback> loadCollectionsCallbackArgumentCaptor;
@Captor
private ArgumentCaptor<CollectionDataSource.SaveOrUpdateCollectionCallback> saveOrUpdateCollectionCallbackArgumentCaptor;
private static List<CardCollection> collections = Lists.newArrayList(new CardCollection("Title1", "Description1", null),
new CardCollection("Title2", "Description2", null));
private final static String collection_title = "title";
@Before
public void setUp() {
// Mockito has a very convenient way to inject mocks by using the @Mock annotation. To
// inject the mocks in the test the initMocks method needs to be called.
MockitoAnnotations.initMocks(this);
// Get a reference to the class under test
presenter = new CollectionPresenter(repository, view);
}
@Test
public void getCollections() {
dataSource.getCollections(loadCollectionsCallback);
verify(dataSource).getCollections(loadCollectionsCallbackArgumentCaptor.capture());
loadCollectionsCallbackArgumentCaptor.getValue().onSuccess(collections);
}
@Test
public void getCollection() {
dataSource.getCollection(collection_title, getCollectionCallback);
verify(dataSource).getCollection(eq(collection_title), any(CollectionDataSourceImplementation.GetCollectionCallback.class));
}
@Test
public void saveCollection() {
CardCollection collection = new CardCollection("Title", "Description", null);
dataSource.saveCollection(collection, saveOrUpdateCollectionCallback);
verify(dataSource).saveCollection(collection, saveOrUpdateCollectionCallbackArgumentCaptor.capture());
saveOrUpdateCollectionCallbackArgumentCaptor.getValue().onSuccess(collection);
}
@Test
public void updateCollection() {
}
@Test
public void deleteCollection() {
}
@Test
public void deleteAllCollections() {
}
}
saveCollection 方法失败。我正在谷歌搜索,主要是寻找如何测试直接 sqlite 连接的方法。任何想法都会有所帮助。谢谢。
我认为错误可能出在这一行:
verify(dataSource).saveCollection(collection, saveOrUpdateCollectionCallbackArgumentCaptor.capture());
更改方式:
verify(dataSource).saveCollection(collection, saveOrUpdateCollectionCallback);
您无法验证具有不同参数的方法。
看来您的单元测试有误:
你的方法是做什么的:
- 测试方法在mocked对象 上调用一个方法
- 然后你 "verify" 调用了模拟方法
简单地说:这没有意义。这个想法是将 mocked 对象传递给 production 代码;然后你触发对生产代码的某种操作。最后,您验证那些您期望看到的调用确实发生了。
除此之外:您不会测试 库 做了它应该做的事情(好吧,至少在单元测试中没有)。
相反,您的单元测试应该只测试您的产品所做的布线。换句话说:您唯一需要测试的是您的生产代码传递给某些库方法调用的参数是否符合预期。
当然,您应该做一些 "integration level" 测试以确保整个过程正常 end-to-end。但这通常 超出 单元测试的范围(正如名称已经告诉我们的那样!)