Search code examples
javaandroidunit-testingmockitoandroid-mvp

Mockito test in MVP pattern


I'm trying to unit test my Presenter class using Mockito and I always end up failing the test:

org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class java.lang.String
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types

This is what my Presenter class looks like:

public class EditorPresenter implements EditorContract.Presenter {

    private DataSource dataSourceImpl;
    private EditorContract.View mView;
    private SharedPreferences prefs;

    EditorPresenter(SharedPreferences prefs,
                    DataSourceImpl dataSourceImpl,
                    EditorContract.View mView) {
        this.dataSourceImpl = dataSourceImpl;
        this.mView = mView;
        this.prefs = prefs;

        mView.setPresenter(this);
    }

    @Override
    public void showNewNote() {
        String noteColor = prefs.getString("default_note_color", "#ef5350");
        String textColor = prefs.getString("default_text_color", "#000000");
        mView.noteColor(Color.parseColor(noteColor));
        mView.textColor(Color.parseColor(textColor));
     }
}

And this is what I've done so far in EditorPresenterTest class:

public class EditorPresenterTest {
    @Mock
    private EditorContract.View mView;
    @Mock
    private DataSourceImpl mDataSourceImpl;
    @Mock
    private SharedPreferences sharedPrefs;
    @Mock
    private String noteColor;
    @Mock
    private String textColor;

    @Before
    public void setUpEditorPresenter() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void createEditorPresenter_newNote() {
        EditorPresenter editorPresenter = new EditorPresenter(
                sharedPrefs,
                mDataSourceImpl,
                mView);
         verify(mView).setPresenter(editorPresenter);
    }
    @Test
    public void showNewNote() {
        when(sharedPrefs.getString(eq("default_note_color"), eq("#ef5350"))).thenReturn(noteColor);
        when(sharedPrefs.getString(eq("default_text_color"), eq("#000000"))).thenReturn(textColor);
        verify(mView).textColor(Color.parseColor(noteColor));
        verify(mView).noteColor(Color.parseColor(textColor));
    }

(Note: I'm new to Mockito and testing) I have passed the createEditorPresenter_newNote() but the showNewNote() failed the test and shows error. Any Feedback/Answers are welcome. Hope someone helps me. Thanks!


Solution

  • I will first answer the exact question that you asked here but keep in mind that you have a larger issue that is hiding behind your compilation error, for which I will provide an answer right after. (please keep in mind that I have no real experience with Android, so exact class names and use cases might not be valid, but your issues are more with understanding what test frameworks do and not syntax-oriented).

    Your first issue is that you are trying to create mock types of the String class, which is final. As you can see in the error from Mockito:

    Mockito cannot mock/spy following:

    - final classes

    In essence, there is no real reason for creating a mock of a String, because you are not testing String functionality. You can just use a constant. if that is what you wish to fix, just remove the @Mock annotations from the noteColor and textColor variables and initialize them with some constant values.


    More about testing frameworks and the other problems you are facing:

    There is another major issue in your test case, and that is that you are trying to use the EditorPresenter you created in the first test inside the scope of the second test.

    The thing is that test frameworks run different tests in separate states (rightfully so). So when you create the EditorPresenter instance inside the createEditorPresenter_newNote method, it won't be visible for you in the showNewNote test method, because it is a different process (not a different CPU process - just a process in the simple day-to-day term of the word).


    What should you be doing instead?

    That's what the before method is for: it will be called before every test runs, so you can set up shared functionality in one place.

    what you should be doing is more on the line of this:

    public class EditorPresenterTest {
        @Mock
        private EditorContract.View mView;
        @Mock
        private DataSourceImpl mDataSourceImpl;
        @Mock
        private SharedPreferences sharedPrefs;
    
        private EditorPresenter editorPresenter;
    
        @Before
        public void setUpEditorPresenter() {
            MockitoAnnotations.initMocks(this);
            this.editorPresenter = new EditorPresenter(
                    sharedPrefs,
                    mDataSourceImpl,
                    mView);
        }
    
        @Test
        public void createEditorPresenter_newNote() {
             verify(mView).setPresenter(editorPresenter);
        }
    
        @Test
        public void showNewNote() {
            editorPresenter.showNewNote();
            String noteColor = "#ef5350"; // or whatever color you want
            String textColor = "#000000"; // or whatever color you want
            when(sharedPrefs.getString(eq("default_note_color"), eq("#ef5350"))).thenReturn(noteColor);
            when(sharedPrefs.getString(eq("default_text_color"), eq("#000000"))).thenReturn(textColor);
            verify(mView).textColor(Color.parseColor(noteColor));
            verify(mView).noteColor(Color.parseColor(textColor));
        }
    }