Android NullPointerException使用模拟上下文创建AppCompatImageView

Android NullPointerException使用模拟上下文创建AppCompatImageView,android,unit-testing,exception,mockito,android-testing,Android,Unit Testing,Exception,Mockito,Android Testing,当我尝试在测试中使用模拟上下文创建AppCompatImageView时,我收到一个NullPointerException。对正常的ImageView进行同样的操作 此测试通过: import android.content.Context; import android.widget.ImageView; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.m

当我尝试在测试中使用模拟
上下文创建
AppCompatImageView
时,我收到一个
NullPointerException
。对正常的
ImageView
进行同样的操作

此测试通过:

import android.content.Context;
import android.widget.ImageView;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;

import static junit.framework.Assert.assertNotNull;

@RunWith(MockitoJUnitRunner.class)
public class ParallaxViewTest {

    @Mock
    Context mContext;

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

    @Test
    public void initWithContext() throws Exception {
        assertNotNull(mContext);
        ImageView imageView = new ImageView(mContext);
//        AppCompatImageView imageView = new AppCompatImageView(mContext);
    }
}
import android.content.Context;
import android.support.v7.widget.AppCompatImageView;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;

import static junit.framework.Assert.assertNotNull;

@RunWith(MockitoJUnitRunner.class)
public class ParallaxViewTest {

    @Mock
    Context mContext;

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

    @Test
    public void initWithContext() throws Exception {
        assertNotNull(mContext);
//        ImageView imageView = new ImageView(mContext);
        AppCompatImageView imageView = new AppCompatImageView(mContext);
    }
}
java.lang.NullPointerException
    at android.support.v7.widget.ResourcesWrapper.<init>(ResourcesWrapper.java:46)
    at android.support.v7.widget.TintResources.<init>(TintResources.java:34)
    at android.support.v7.widget.TintContextWrapper.<init>(TintContextWrapper.java:100)
    at android.support.v7.widget.TintContextWrapper.wrap(TintContextWrapper.java:68)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:60)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:56)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:52)
    at example.views.ParallaxViewTest.initWithContext(ParallaxViewTest.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:68)
    at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:74)
    at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
    at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.7.18'
此测试未通过:

import android.content.Context;
import android.widget.ImageView;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;

import static junit.framework.Assert.assertNotNull;

@RunWith(MockitoJUnitRunner.class)
public class ParallaxViewTest {

    @Mock
    Context mContext;

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

    @Test
    public void initWithContext() throws Exception {
        assertNotNull(mContext);
        ImageView imageView = new ImageView(mContext);
//        AppCompatImageView imageView = new AppCompatImageView(mContext);
    }
}
import android.content.Context;
import android.support.v7.widget.AppCompatImageView;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;

import static junit.framework.Assert.assertNotNull;

@RunWith(MockitoJUnitRunner.class)
public class ParallaxViewTest {

    @Mock
    Context mContext;

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

    @Test
    public void initWithContext() throws Exception {
        assertNotNull(mContext);
//        ImageView imageView = new ImageView(mContext);
        AppCompatImageView imageView = new AppCompatImageView(mContext);
    }
}
java.lang.NullPointerException
    at android.support.v7.widget.ResourcesWrapper.<init>(ResourcesWrapper.java:46)
    at android.support.v7.widget.TintResources.<init>(TintResources.java:34)
    at android.support.v7.widget.TintContextWrapper.<init>(TintContextWrapper.java:100)
    at android.support.v7.widget.TintContextWrapper.wrap(TintContextWrapper.java:68)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:60)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:56)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:52)
    at example.views.ParallaxViewTest.initWithContext(ParallaxViewTest.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:68)
    at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:74)
    at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
    at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.7.18'
这是崩溃报告:

import android.content.Context;
import android.widget.ImageView;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;

import static junit.framework.Assert.assertNotNull;

@RunWith(MockitoJUnitRunner.class)
public class ParallaxViewTest {

    @Mock
    Context mContext;

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

    @Test
    public void initWithContext() throws Exception {
        assertNotNull(mContext);
        ImageView imageView = new ImageView(mContext);
//        AppCompatImageView imageView = new AppCompatImageView(mContext);
    }
}
import android.content.Context;
import android.support.v7.widget.AppCompatImageView;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;

import static junit.framework.Assert.assertNotNull;

@RunWith(MockitoJUnitRunner.class)
public class ParallaxViewTest {

    @Mock
    Context mContext;

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

    @Test
    public void initWithContext() throws Exception {
        assertNotNull(mContext);
//        ImageView imageView = new ImageView(mContext);
        AppCompatImageView imageView = new AppCompatImageView(mContext);
    }
}
java.lang.NullPointerException
    at android.support.v7.widget.ResourcesWrapper.<init>(ResourcesWrapper.java:46)
    at android.support.v7.widget.TintResources.<init>(TintResources.java:34)
    at android.support.v7.widget.TintContextWrapper.<init>(TintContextWrapper.java:100)
    at android.support.v7.widget.TintContextWrapper.wrap(TintContextWrapper.java:68)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:60)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:56)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:52)
    at example.views.ParallaxViewTest.initWithContext(ParallaxViewTest.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:68)
    at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:74)
    at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
    at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.7.18'

我如何解决这个问题?


编辑

如何使用资源获取模拟的
上下文

此测试未通过:

    @Test
    public void initWithContext() throws Exception {
        assertNotNull(mContext); // PASS
        assertNotNull(mContext.getResources()); // DO NOT PASS
//        ImageView imageView = new ImageView(mContext);
//        AppCompatImageView imageView = new AppCompatImageView(mContext);
    }

当您转到堆栈跟踪()中列出的类的源代码时,您会发现:

public ResourcesWrapper(Resources resources) {
  super(resources.getAssets(), resources.getDisplayMetrics(), 
第46行是带super()的那一行

进一步查看堆栈跟踪中的类,您可能会发现:

 private TintContextWrapper(@NonNull final Context base) {
  super(base);
  ...
  mResources = new VectorEnabledTintResources(this, base.getResources());
因此,长话短说,是的,您在代码中为
新的AppCompatImageView()
提供了一个非null的模拟对象。但是,您调用的代码正在对该模拟对象调用方法。当然,这就是为什么您首先创建了一个mock。但是你猜怎么着;默认情况下,模拟框架将为任何方法调用返回null

换句话说:您必须了解该模拟将发生哪些调用;这样您就可以准备mock来返回非null的内容了

确切地说:我并不是说TintContextWrapper()中的那一行导致了这个NPE;我的主要意思是:当您将一个模拟对象放入其他代码中时,您必须准备该模拟,以便对将要发生的方法调用返回合理的结果。这很可能意味着您必须创建更多的模拟;因此类似于
mockedContext.getResources()
的内容会返回一个非空的结果

换句话说,你必须

  • 识别在该模拟对象上发生的那些调用
  • 然后,您必须确保这些调用将返回非null(例如,再次返回模拟对象)
除此之外:更可能的是,真正的答案是使用特定于Android的模拟框架。准备你的模拟让他们“做正确的事情”很容易就会变成很多工作

也许最简单的答案是使用mockito的方法,简单地写下

@Mock (answer = Answers.RETURNS_DEEP_STUBS)
但是你需要阅读/尝试一下;我自己没用过

考虑到您的最新版本:您需要配置您的模拟,如

when(context.getResources()).thenReturn(someOtherMock);

比如说!这就是mock的要点:您可以控制调用方法时发生的事情

您正在解释如何搜索解决方案,我对此表示感谢,但我认为答案应该更简单,因为我正在使用Android文档库进行测试,并创建一个简单的AppCompatImageView。这应该不难。ImageView工作得很好,为什么AppCompatImageView没有呢?我假设我不是唯一一个有这个问题的人,但是在你最后的评论之后,答案是如何简单地将资源设置到上下文中。是的,我找到了when().thenReturn()功能。这正是我在寻找的。谢谢,你可能想检查一下,如果深存根可能会自动做到这一点;但也许我也只是把这个功能搞错了。很高兴你得到了你想要的东西!最后一个提示:你做得太过分了。您已经在使用Mockito运行程序了,因此调用'MockitoAnnotations.initMocks(this);`一点也不需要。只有在出于某种原因不使用Mockito运行程序时,才需要手动初始化mocks()。因此,实际上根本不需要“setup”方法(到目前为止,这可能会发生变化,例如,当您想在setup方法中加入一些通用when/then子句时;因为您的所有测试都需要它们)