Android junit4打开getResources()。使用mockito的openRawResource运行nullpointer

Android junit4打开getResources()。使用mockito的openRawResource运行nullpointer,android,mockito,junit4,Android,Mockito,Junit4,我正在创建一个junit4单元测试,以测试是否打开原始目录中包含的文件 但是,每次代码运行时,我都可以从openrawsource中找到一个空指针 这就是我要测试的功能。在实际设备上运行时,此功能有效。但不是在单元测试中 android studio 2.1. preview 4 这是我的测试用例 public String getNewsFeed(Context mContext) { InputStream inputStream = mContext.getResources()

我正在创建一个
junit4单元测试
,以测试是否打开原始目录中包含的文件

但是,每次代码运行时,我都可以从
openrawsource
中找到一个空指针

这就是我要测试的功能。在实际设备上运行时,此功能有效。但不是在单元测试中

android studio 2.1. preview 4
这是我的测试用例

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

非常感谢您的建议,

您必须告诉mContext mock在调用getResources()时该怎么做

@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));
    }
}
如果不指定任何内容,mock将返回null


对于您的示例,这意味着您可能还需要一个资源模拟,并在调用openRawResource()时告诉它何时执行/返回。

您已经创建了一个模拟
上下文
,因此您必须存根被测方法使用的方法。 在
getNewsFeed
中,您正在使用
Context::getResources
,因此在调用
getNewsFeed
之前的测试中,您应该:

when(mContext.getResources()).thenReturn(some_mock_of_Resources);
问题很清楚


在你的测试中,你也有一些问题。我认为它们没有任何后果,但它们往往表明你正在发现莫基托

你写道:

Resources resoures = ...
when(mContext.getResources()).thenReturn(resources);
@Mock
注释字段,告诉mockito创建一个
NewsListPresenter
的Mock,该类将被测试。(这不是一个大问题,因为您在
设置中创建了真实的实例。你可以去看看(即使)

另一点是,您同时使用
@RunWith(MockitoJUnitRunner.class)
MockitoAnnotations.initMocks(this)
,您可以避免最后一个,因为它是由运行程序调用的。此外,跑步者是一个更好的选择,因为它


我最后的观察是关于你的设计。这可能是一个android约束,但您正在presenter构造函数和
getNewsFeed
方法中注入上下文,这看起来很奇怪,可能会导致不一致的情况。我会选择构造函数注入(更面向对象的设计)

另一种简化测试的方法是提取实用程序类中方法的主要部分,并测试不同的分支(空流、空流、有效流、IOException…),而无需模拟上下文和资源:

@Mock
private NewsListPresenter mNewsListPresenter;

请注意,guava提供了一个解决方案。

您混淆了Android中的两种单元测试。这对很多人来说并不清楚,所以我会在这里解释

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

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

我想您想让它作为junit测试运行,以更快地运行它,这就是为什么我想您将测试移动到“src/test”文件夹和上下文中。getResources()引发了NullPointerException

我认为你有两个选择:

  • 用于将此测试作为junit测试运行
  • 重构您的方法,使其不依赖于Android的类
  • 对于选项2,我会这样做。将方法的参数更改为InputStream:

    class IOUtils {
        static String readFromStream(InputStream inputStream) {
            ....
        }
    }
    
    现在,您的方法看不到任何Android代码,您可以将其作为普通junit方法进行测试。然后,您可以将一个伪inputStream传递给您的方法,如下所示:

    public String getNewsFeed(InputStream inputStream) {... use inputStream... }
    
    如果您仍然希望通过与您在应用程序中使用的相同的inputStream(我不建议这样做),您仍然可以在测试中使用以下代码:

    @Test
    public void testLoadNewsFeed() throws Exception {
        String fileContents = "line one";
        InputStream inputStream = new ByteArrayInputStream(fileContents.getBytes());
        assertNotNull(mNewsListPresenter.getNewsFeed(inputStream));
    }
    
    您必须将这一行添加到build.gradle文件中:

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

    Android单元测试可能会非常混乱。你可以在我写的博客文章中读到这些概念

    你能说得更具体些吗。我正在这样做:when(mContext.getResources())。然后return(R.raw.news\u list);但是R.raw.news_列表未被识别。你能给我一些更多的指导吗?如果这是你第一次用mock做单元测试,你可能想按照mockito的一些教程开始。@ant2009当(mContext.getResources())时你在哪里做
    。然后返回(R.raw.news_list)
    ?它不在您发布的代码中。@ant2009您不能为mContext.getResources()返回int,您需要返回一个资源实例(或像Yair建议的那样使用)。调用getResources时,我已尝试返回一个模拟资源。但是,我无法使用如此模拟的资源访问字符串或drawables。when(context.getResources()).thenReturn(PowerMockito.mock(Resources.class));String str=context.getResources().getString(R.String.app_name);返回的str值为null。
    sourceSets.test.resources.srcDirs += ["src/main"]