I am receiving a NullPointerException
when I try to create an AppCompatImageView
with a mock Context
in a test. Doing the same with a normal ImageView
works.
This test pass:
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);
}
}
This test DOES NOT pass:
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);
}
}
And this is the crash report:
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)
Here the libraries:
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.7.18'
How can I solve this?
EDIT
How can I get a mock Context
with resources?
This test DOES NOT pass:
@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);
}
When you turn to the source code of the class listed in the stack trace (ResourcesWrapper), you find:
And that line 46 is the one with super().
Further looking into the classes in your stack trace, you could come accross:
So, long story short, yes, you are providing a non null mock object to
new AppCompatImageView()
in your code. But then the code you are calling is calling methods on that mocked object. Sure, that is why you created a mock in the first place. But guess what; by default, the mocking framework will return null for any method call.In other words: you have to understand which calls will happen on that mock; so that you can prepare the mock to return something non null too!
To be precise: I am not saying that exactly that line from TintContextWrapper() causes this NPE; I am mainly saying: when you give a mocked object into other code, you have to prepare that mock to return reasonable results on those method calls that will happen. That could very well mean that you have to create more mocks; so that something like
mockedContext.getResources()
does return a non-null result.In other words: you have to
Beyond that: more likely, the real answer is to use Android specific mocking frameworks. Preparing your mocks to get them "do the right thing" could easily turn into a lot of work.
Maybe the simple answer is to use the deep stubbing from mockito, by simply writing
But you need to read/try that; I haven't used that myself.
And given your latest: you need to configure your mock, like
for example! That is the whole point of mocks: you can control what happens when methods are called!