MVP 模式中的 Mockito 测试

Mockito test in MVP pattern

我正在尝试使用 Mockito 对我的 Presenter class 进行单元测试,但我总是以失败告终:

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

这是我的 Presenter class 的样子:

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

这就是我到目前为止在 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));
    }

(注意:我是 Mockito 和测试的新手) 我通过了 createEditorPresenter_newNote()showNewNote() 未通过测试并显示错误。欢迎任何Feedback/Answers。希望有人帮助我。谢谢!

我将首先回答您在这里提出的确切问题,但请记住,您的编译错误背后隐藏着一个更大的问题,我将在之后立即提供答案。 (请记住,我没有使用 Android 的实际经验,因此确切的 class 名称和用例可能无效,但您的问题更多是了解测试框架的作用,而不是面向语法的).

您的第一个问题是您正在尝试创建 String class 的模拟类型,这是最终的。正如您在 Mockito 的错误中看到的那样:

Mockito cannot mock/spy following:

- final classes

本质上,没有真正的理由创建字符串的模拟,因为您不是在测试字符串的功能。你可以只使用一个常量。 如果这是你想修复的,只需从 noteColortextColor 变量中删除 @Mock 注释并用一些常量值初始化它们。


有关测试框架和您面临的其他问题的更多信息:

您的测试用例中还有另一个主要问题,那就是您正试图在第二个测试的范围内使用您在第一个测试中创建的EditorPresenter

问题是测试框架 运行 在不同的状态下进行不同的测试(理所当然)。因此,当您在 createEditorPresenter_newNote 方法中创建 EditorPresenter 实例时,它在 showNewNote 测试方法中对您不可见,因为它是一个不同的过程(不是不同的 CPU 过程 - 只是简单日常术语中的过程)。


你应该做什么?

这就是 before 方法的用途:它会在每次测试 运行 之前调用,因此您可以在一个地方设置共享功能。

你应该做的更多的是这个:

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