This question already has answers here:
How to mock a final class with mockito
(28 answers)
Closed 5 years ago.
I have one class which is final, which has one method on which I want to perform certain action. Because of this I want to create object of final class. But I am unable to create it, following is my class.
public final class A {
private String name;
A(String name){
this.name = name;
}
public String getName(){
return name;
}
}
In my junit test case I want to create object of that class, like below
Class TestA{
#Test
public void testA(){
A a = mock(A.class);
when(a.getName()).then("ABC"); //on this line i am getting exception
}
}
I have tried it by using new keyword also, but not working. So is there anyway to create a mock object of final class?
Following exception I facing,
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class A
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
at com.rocket.map.resources.TestA.testA(TestA.java:46)
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:498)
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.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
try using this.
Use the #RunWith(PowerMockRunner.class) annotation at the class-level of the test case.
Use the #PrepareForTest(ClassWithFinal.class) annotation at the class-level of the test case.
Use PowerMock.createMock(ClassWithFinal.class) to create a mock object for all methods of this class (let's call it mockObject).
Use PowerMock.replay(mockObject) to change the mock object to replay mode.
Use PowerMock.verify(mockObject) to change the mock object to verify mode.
Also refer this answer - link
And Tutorial.
Both look easy to implement.
This is not possible with Mockito v1
Please look into this link. I think in advance version or powermockito you can do this.Powermockito example
Related
so I'm building a test class and have the following setup function:
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
//do other set up stuff
}
When I run the test class without MockitoAnnotations.initMocks(this);, everything works as expected. However, as soon as I want to add mocks and add the initMocks statement, I get the following error message:
Cannot mock/spy class java.lang.String
Mockito cannot mock/spy because :
- final class
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class java.lang.String
Mockito cannot mock/spy because :
- final class
at org.mockito.internal.runners.DefaultInternalRunner$1.withBefores(DefaultInternalRunner.java:38)
at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:276)
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:78)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:84)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
It's so confusing because I don't believe I used strings in there? I stripped down the class to a single test asserting that 1 == 1 so I know for a fact the error doesn't come from the tests. FYI, that also happens when I try to use #RunWith(MockitoJUnitRunner.class). Any pointers? Thanks!
I have a JUnit test where I have 5 mocks and one of mocked classes is final, I enabled mock-maker-inline to overcome that problem and now test passes but at the end of test execution I also get NotAMockException at:
org.mockito.exceptions.misusing.NotAMockException: Argument passed to Mockito.mockingDetails() should be a mock, but is an instance of class ...Locals!
at org.mockito.internal.runners.DefaultInternalRunner$1$2.testFinished(DefaultInternalRunner.java:63)
at org.junit.runner.notification.SynchronizedRunListener.testFinished(SynchronizedRunListener.java:56)
at org.junit.runner.notification.RunNotifier$7.notifyListener(RunNotifier.java:190)
at org.junit.runner.notification.RunNotifier$SafeNotifier.run(RunNotifier.java:72)
at org.junit.runner.notification.RunNotifier.fireTestFinished(RunNotifier.java:187)
at org.junit.internal.runners.model.EachTestNotifier.fireTestFinished(EachTestNotifier.java:38)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:331)
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:74)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:80)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
...
The mocking is very simple:
#Mock
private Locals locals; //class
#Mock
private Globals globals; // class
#Mock
private Cache cache; // interface
#Mock
private Reminds reminds; // final class
#Mock
private Employeereminds employeereminds; // class
... followed by couple of when().thenReturn() setups to make it work together
During the test mocks looks like this:
locals = {Locals#3233}
globals = {Globals#3234}
cache = {Cache$MockitoMock$665734991#3237} "cache"
reminds = {Reminds#3235}
employeereminds = {Employeereminds#3236}
I am using Mockito 2.25.0 also tried 2.28.2 but no difference.
I can refactor my test to also test the content of final class but I prefer my test isolated. I could also use powermock but have seen similar problems reported with powermock.
Did anybody stumble upon such issue and found a decent workaround?
EDIT: I did some more investigation, I removed Locals Mock and got same error on Globals mock. Then I removed Globals mock and got (!?) following exception (# v2.28.2):
org.mockito.exceptions.misusing.NotAMockException: Argument passed to Mockito.mockingDetails() should be a mock, but is an instance of class Cache$MockitoMock$149288076!
at org.mockito.internal.runners.DefaultInternalRunner$1$2.testFinished(DefaultInternalRunner.java:63)
at org.junit.runner.notification.SynchronizedRunListener.testFinished(SynchronizedRunListener.java:56)
at org.junit.runner.notification.RunNotifier$7.notifyListener(RunNotifier.java:190)
at org.junit.runner.notification.RunNotifier$SafeNotifier.run(RunNotifier.java:72)
at org.junit.runner.notification.RunNotifier.fireTestFinished(RunNotifier.java:187)
at org.junit.internal.runners.model.EachTestNotifier.fireTestFinished(EachTestNotifier.java:38)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:331)
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:74)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:80)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
...
Ok, I've found the issue:
I was invoking
Mockito.framework().clearInlineMocks();
in a code invoked by #After annotation, but the correct way is to do it in code invoked by #AfterClass annotation, otherwise Mockito own verification is run after you cleared the mocks.
I have a class that I would like to test.
#Configuration
#Import(EmailageConfiguration.class)
public class EmailageServiceConfiguration {
private static final String EMAILAGE_ACCOUNT_ID_CONFIG_KEY = "emailage.key";
private static final String EMAILAGE_API_KEY_CONFIG_KEY = "emailage.secret";
#Bean
public EmailageConfigHolder emailageConfigHolder(Environment env) {
final EmailageConfigHolder holder = new EmailageConfigHolder();
holder.setApiKey(env.getRequiredProperty(EMAILAGE_API_KEY_CONFIG_KEY));
holder.setAccountId(env.getRequiredProperty(EMAILAGE_ACCOUNT_ID_CONFIG_KEY));
return holder;
}
}
My test class is provided,
#RunWith(MockitoJUnitRunner.class)
public class EmailageServiceConfigurationTest {
#InjectMocks
private EmailageServiceConfiguration configuration;
#Mock
private Environment environment;
#Mock
private EmailageConfigHolder holder;
#Test
public void testEmailageConfigHolder() {
when(environment.getRequiredProperty(anyString())).thenReturn(anyString());
configuration.emailageConfigHolder(environment);
verify(holder, times(1)).setApiKey(anyString());
verify(holder, times(1)).setAccountId(anyString());
}
}
I get the error stack provided below,
Wanted but not invoked:
holder.setApiKey();
-> at com.ratepay.ella.service.config.EmailageServiceConfigurationTest.testEmailageConfigHolder(EmailageServiceConfigurationTest.java:48)
Actually, there were zero interactions with this mock.
Wanted but not invoked:
holder.setApiKey();
-> at com.ratepay.ella.service.config.EmailageServiceConfigurationTest.testEmailageConfigHolder(EmailageServiceConfigurationTest.java:48)
Actually, there were zero interactions with this mock.
at com.ratepay.ella.service.config.EmailageServiceConfigurationTest.testEmailageConfigHolder(EmailageServiceConfigurationTest.java:48)
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:498)
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.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:79)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:85)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
How do I correct the test?
Here:
final EmailageConfigHolder holder = new EmailageConfigHolder();
Mockito can't inject mocks into a local variable. The documentation is really clear about that:
Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection in order and as described below.
Basically, by using new() within the body of your method you wrote hard to test code. Because with Mockito, you have zero options to control what new() will return in that method body.
Ways out of that:
make that "holder" a field of your class, then inject via that annotation, or via a constructor accepting a holder instance
pass an instance as parameter to the method
Or assuming that you can actually create a new Holder object within the production code within your unit test setup, and as you are returning that object, simply assert on the properties of the returned object. From that point of view, you do not need to using mocking here at all. Simply verify that the object coming back from that call has the expected properties!
Or, (not recommended) you could turn to PowerMock(ito) or JMockit, in order to gain control over that call to new(). But as said: better rework your code to be easy to test.
By the way: the real answer is that you step back and read a good tutorial about Mockito. You can't learn how to use such a framework by trial and error. Learn how to do it right with nice small examples, and then, when you understand how to connect the dots, then apply that to your own code!
While the other answer better fits with the situation, I'm not able to update the code and finally, wrote this test code.
#RunWith( MockitoJUnitRunner.class )
public class EmailageServiceConfigurationTest {
private static final String ACCOUNT_ID = "emailage.key";
private static final String API_KEY = "emailage.secret";
#InjectMocks
private EmailageServiceConfiguration configuration;
#Mock
private Environment environment;
#Test
public void testEmailageConfigHolder() {
configuration.emailageConfigHolder( environment );
verify( environment, times( 1 ) ).getRequiredProperty( API_KEY );
verify( environment, times( 1 ) ).getRequiredProperty( ACCOUNT_ID );
}
}
I'm using Room on Android. So I've got my data model class with the proper Room annotations and a single public constructor. It uses a builder pattern and tries to be immutable. It's here.
Then I've got my #Dao-annotated interface with #Query, #Update, and #Insert annotations next to all the method declarations as it should be. Right over here.
Finally, I've got my Dao test class, which uses Room.inMemoryDatabaseBuilder. It is here.
So when I run the tests in GoalsDaoTest, they all pass except for two: One that uses the Dao method annotated #Update, and another whose Dao method uses an SQL UPDATE command inside its #Query.
Here's my test set-up and tear-down, using Room.inMemoryDatabaseBuilder:
#Before
public void initDb() {
mDatabase = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(),
SelfCareDatabase.class).build();
}
#After
public void closeDb() {
mDatabase.close();
}
Here's the first failing test:
#Test
public void updateArchivedAndGetById() {
// When inserting a goal
mDatabase.goalDao().insertGoal(GOAL);
// When the goal is updated
mDatabase.goalDao().updateArchived(GOAL.getId(), true);
// When getting the goal by id from the database
Goal loaded = mDatabase.goalDao().getGoalById(ID);
// The loaded data contains the expected values
assertGoal(loaded, GOAL.getId(), GOAL.getTitle(), GOAL.getInterval(), GOAL.getPolarity(), true, GOAL.getTouched());
}
Here's the error it produces when I run it:
java.lang.NoSuchMethodError: No interface method updateArchived(Ljava/lang/String;Z)V in class Lcom/beatboxchad/android/selfcaredashboard/data/source/local/GoalsDao; or its super classes (declaration of 'com.beatboxchad.android.selfcaredashboard.data.source.local.GoalsDao' appears in /data/app/com.beatboxchad.android.selfcaredashboard-1/base.apk)
at com.beatboxchad.android.selfcaredashboard.data.source.local.GoalsDaoTest.updateArchivedAndGetById(GoalsDaoTest.java:155)
at java.lang.reflect.Method.invoke(Native Method)
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 android.support.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
at android.support.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61)
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.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
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.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:58)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:375)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1939)
And yet, that method is properly declared and annotated in the GoalsDao class:
/**
* Update the Archived status of a goal
*
* #param goalId id of the goal
* #param archived status to be updated
*/
#Query("UPDATE goals SET archived = :archived WHERE entryid = :goalId")
void updateArchived(String goalId, boolean archived);
Here's the other test:
#Test
public void updateGoalAndGetById() {
// When inserting a goal
mDatabase.goalDao().insertGoal(GOAL);
// When the goal is updated
Goal updatedGoal = new Goal.Builder(ID)
.setTitle(TITLE2)
.setPolarity(POLARITY2)
.setInterval(INTERVAL2)
.setArchived(ARCHIVED2)
.setTouched(TOUCHED2)
.build();
mDatabase.goalDao().updateGoal(updatedGoal);
// When getting the goal by id from the database
Goal loaded = mDatabase.goalDao().getGoalById(ID);
// The loaded data contains the expected values
assertGoal(loaded, ID, TITLE2, INTERVAL2, POLARITY2, ARCHIVED2, TOUCHED2);
}
Here's its error:
java.lang.NoSuchMethodError: No interface method updateGoal(Lcom/beatboxchad/android/selfcaredashboard/data/Goal;)I in class Lcom/beatboxchad/android/selfcaredashboard/data/source/local/GoalsDao; or its super classes (declaration of 'com.beatboxchad.android.selfcaredashboard.data.source.local.GoalsDao' appears in /data/app/com.beatboxchad.android.selfcaredashboard-1/base.apk)
at com.beatboxchad.android.selfcaredashboard.data.source.local.GoalsDaoTest.updateGoalAndGetById(GoalsDaoTest.java:140)
at java.lang.reflect.Method.invoke(Native Method)
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 android.support.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
at android.support.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61)
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.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
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.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:58)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:375)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1939)
And yet! Confound me! What madness causes the computer to lie? GoalsDao.java excerpt:
/**
* Update a goal.
*
* #param goal goal to be updated
* #return the number of goals updated. This should always be 1.
*/
#Update
int updateGoal(Goal goal);
I've exhausted all the low-hanging fruit on this one. Everything appears to conform to the docs (though I'm suspicious of my immutable builder'd Goal class), and I've attempted to follow advice given in the following places:
Why Room entities don't work with immutable properties in Android
Espresso Test Failing: No interface method trackUsage() in UsageTracker.java
Room Persistence Library run time exception when calling Rooms inMemoryBuilder method
But I'm not using Kotlin nor the #Relation annotation, and even when I played with all kinds of different versions in my build.gradle files, it stayed broken. The bright side is, I've learned a lot about that boring part. tl;dr I'm stumped.
Just in case it's helpful context, my app is strongly based on the todo‑mvvm‑databinding example from https://github.com/googlesamples/android-architecture. I mention this because everything works in the sample, but I've added some complexity and perhaps made a mistake along the way that somebody more experienced will recognize. Particularly, I've mangled that #Entity Goal class (Task in the example app), trying to keep it immutable and adding the builder pattern and several attributes. I'm not done mangling it either -- touched is about to be its own table.
I'm also unskilled in troubleshooting whatever turns an interface and a bunch of annotations into code. Any advice about where to look in resolving this is as useful as the answer it'll lead to. I'm new to Android Studio, Java, and the whole build process.
Thank you!
Invalidate caches and restart in android studio helped
I was adding suspend to some functions in the DAO and ran into this. A simple Build\Clean fixed it
I use a Hystrix (v. 1.5.6) command in my tested class, and I'm trying to mock it with Mockito (v. 1.10.19).
Recently I've had to add a special behavior if the command failed because of a timeout:
try {
result = command.execute();
} catch (HystrixRuntimeException err) {
if (command.isResponseTimedOut()) {
/** do stuff **/
}
/** do stuff **/;
}
This is the code in my test class :
HttpClientCommand command = mock(HttpClientCommand.class);
when(commandFactory.get(anyString(), anyString(), anyString(), anyMapOf(String.class, String.class))).thenReturn(command);
when(command.execute()).thenThrow(hystrixRuntimeException);
when(command.isResponseTimedOut()).thenReturn(false);
My HttpClientCommand extends the HystrixCommand class. The execute method is in this class, and I have no problem stubbing it.
The HystrixCommand class then extends the AbstractCommand class, where the isResponseTimedOut method is. When I try to stub it, I get a NPE from Mockito (on the when(command.isResponseTimedOut()).thenReturn(false); line, before calling any method on my tested class).
Stack trace :
java.lang.Exception: Unexpected exception, expected<my.custom.exception> but was<java.lang.NullPointerException>
at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:28)
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.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
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)
Caused by: java.lang.NullPointerException
at com.netflix.hystrix.AbstractCommand.isResponseTimedOut(AbstractCommand.java:1809)
at com.netflix.hystrix.HystrixCommand.isResponseTimedOut(HystrixCommand.java:46)
at com.myClassTest.testGetLocation_shouldThrowCustomException_whenRequestException(myClassTest.java:300)
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.ExpectException.evaluate(ExpectException.java:19)
... 22 more
How can I stub this method?
OK the issue is due to a bug with CGLIB, mockito 1.x uses CGLIB. When the method to mock is declared in a non public parent, then CGLIB doesn't intercept the method, hence it executes the real code. The issue is still available on our old issue tracker : issue 212
Two options :
Write a public overload in HttpHystrixCommand that will invoke the super method, but since the abstract command have a lot of methods to handle failure, this is probalby not an option that will scale very well.
Use mockito 2. We have worked with the developer of ByteBuddy – Rafel Winterhalter – to replace CGLIB. ByteBuddy does not have the same shortcomings of CGLIB which was almost abandonned for years. Current version is 2.2.29 and at the time of this anwer regular updates are released.