junit4打开一个getResources().使用mockito的openRawResource运行nullpointer

ant*_*009 7 android junit4 mockito

android studio 2.1. preview 4
Run Code Online (Sandbox Code Playgroud)

我正在创建一个junit4 unit test测试来打开原始目录中包含的文件.

但是,每次代码运行时我都可以使用空指针openRawResource.

这是我试图测试的功能.这在实际设备上运行时有效.但不是在单元测试中.

public String getNewsFeed(Context mContext) {
    InputStream inputStream = mContext.getResources().openRawResource(R.raw.news_list); // Null pointer

    Writer writer = new StringWriter();
    char[] buffer = new char[1024];

    try {
        InputStreamReader inputReader = new InputStreamReader(inputStream, "UTF-8");
        BufferedReader bufferReader = new BufferedReader(inputReader);
        int n;
        while ((n = bufferReader.read(buffer)) != -1) {
            writer.write(buffer, 0, n);
        }

        inputStream.close();
    }
    catch (IOException ioException) {
        return "";
    }

    return writer.toString();
}
Run Code Online (Sandbox Code Playgroud)

这是我的测试用例

@RunWith(MockitoJUnitRunner.class)
public class NewsListPresenterTest {

    @Mock
    private Context mContext;
    @Mock
    private NewsListPresenter mNewsListPresenter;

    @org.junit.Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mNewsListPresenter = new NewsListPresenter(mContext);
    }

    @org.junit.Test
    public void testLoadNewsFeed() throws Exception {
        assertNotNull(mNewsListPresenter.getNewsFeed(mContext));
    }
}
Run Code Online (Sandbox Code Playgroud)

非常感谢任何建议,

Yai*_*lka 7

您在Android中混淆了两种类型的单元测试.这对许多人来说并不清楚,所以我将在这里解释一下.

为什么它在设备上运行:因为这是一个仪器化测试.什么是仪器化测试?在真实设备/模拟器上运行的测试,测试代码位于'src/androidTest'文件夹中.

为什么它不能作为本地junit测试:因为本地junit测试不是仪器测试.本地Junit测试在计算机的JVM上运行,而不是在设备上运行.本地Junit测试不应包含/使用Android代码,因为真正的Android代码位于设备/模拟器上,而不是计算机的JVM上.

我想你想让它作为junit测试运行以更快地运行它,这就是为什么我认为你将测试移动到'src/test'文件夹并且context.getResources()抛出了NullPointerException.

我想你有两个选择:

  1. 使用Robolectric作为junit测试运行此测试
  2. 重构您的方法,使其不依赖于Android的类

对于选项2,这就是我要做的.将方法的参数更改为InputStream:

public String getNewsFeed(InputStream inputStream) {... use inputStream... }
Run Code Online (Sandbox Code Playgroud)

现在您的方法没有看到任何Android代码,您可以将其作为普通的junit方法进行测试.然后你可以将假的inputStream传递给你的方法,如下所示:

@Test
public void testLoadNewsFeed() throws Exception {
    String fileContents = "line one";
    InputStream inputStream = new ByteArrayInputStream(fileContents.getBytes());
    assertNotNull(mNewsListPresenter.getNewsFeed(inputStream));
}
Run Code Online (Sandbox Code Playgroud)

如果您仍想传递与在应用程序中使用相同的inputStream(我不推荐它),您仍然可以在测试中使用此代码执行此操作:

@Test
    public void testLoadNewsFeed() throws Exception {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("raw/news_list");
        assertNotNull(mNewsListPresenter.getNewsFeed(inputStream));
    }
Run Code Online (Sandbox Code Playgroud)

并且您必须将此行添加到build.gradle文件中:

sourceSets.test.resources.srcDirs += ["src/main"]
Run Code Online (Sandbox Code Playgroud)

Android单元测试可能非常混乱.您可以在我写的这篇博文中了解这些概念


Dou*_*son 6

你必须告诉mContext模拟在调用getResources()时要做什么.

when(mContext.getResources()).thenReturn(some_mock_of_Resources);
Run Code Online (Sandbox Code Playgroud)

如果未指定任何内容,则mock将返回null.

对于您的示例,这意味着您可能还需要一个Resources mock,并且还告诉它何时执行/返回当调用openRawResource()时.