4

I am new to Mockito testing in Android and have a problem understanding how to test the data source methods. I use RushOrm that is initialized in the Application class of the app:

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);

My datasource class is this:

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);
    }
}

This is my test class for the Repository 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() {

    }
}

The saveCollection method is failing. I am googling and mostly finding how to test a direct sqlite connection. Any ideas would be helpful. Thank you.

Gabi Radu
  • 1,107
  • 2
  • 16
  • 35
  • Hi, What is the goal of your test? At which point are you mocking. I don't have much knowledge of Mockito but can help you under what RushOrm is doing. – Stuart Campbell Jan 27 '17 at 12:55
  • Hi, I want to test the operations with RushOrm from the app are working properly. – Gabi Radu Jan 30 '17 at 16:33
  • 1
    If you want to test the actually library you don't really want to be mocking anything. You can test it using the standard android test framework. You might want to mock your own classes but I don't think you want to mock the datasource stuff because then you are mocking what you actually want to test. RushOrm has a load of tests check them out might help you get started https://github.com/Stuart-campbell/RushOrm/tree/master/RushORM/rushandroid/src/androidTest/java/co/uk/rushorm/android – Stuart Campbell Jan 31 '17 at 16:50

2 Answers2

3

It looks like you are getting unit testing wrong:

What your methods are doing is:

  • the test method calls a method on the mocked object
  • then you "verify" that the mocked method was called

Simply spoken: that doesn't make sense. The idea is that you pass mocked objects to your production code; and then you trigger some kind of operation on the production code. And finally, you verify that those calls that you expected to see did really happen.

And beyond that: you don't test that a library does what it is supposed to do (well, at least not in unit tests).

Instead, your unit test should only test the wiring that your production does. In other words: the only thing you have to test is that the arguments that your production code passes to some library method call are the expected ones.

Of course, you should do some "integration level" testing to ensure that the whole thing works end-to-end. But that is typically beyond the scope of a unit test (as the names already tell us!)

GhostCat
  • 137,827
  • 25
  • 176
  • 248
1

I think that the error could be at this line:

verify(dataSource).saveCollection(collection, saveOrUpdateCollectionCallbackArgumentCaptor.capture());

Change by:

verify(dataSource).saveCollection(collection, saveOrUpdateCollectionCallback);

You can't verify a method with a different parameter.

antonicg
  • 934
  • 10
  • 24