1

I'm trying to create an unit test mocking some dependencies with Mockito. Here's the main code:

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

import static org.mockito.Mockito.verify;

@RunWith(MockitoJUnitRunner.class)
public class VolumesPresenterTest {

VolumesPresenter presenter;

@Mock
OrderRepository orderRepository;

@Mock
VolumesRepository volumesRepository;

@Before
public void createPresenter() {
    presenter = new DefaultVolumesPresenter(orderRepository, volumesRepository);
}

@Test
public void testGetVolumes() {
    presenter.getVolumes();
    verify(volumesRepository).getVolumes();
}
}

OrderRepository has a single constructor, which receives an Android Context as a param. This is causing the unit test to fail with the following stack trace:

java.lang.NoClassDefFoundError: android/content/Context

at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getDeclaredConstructors(Class.java:2020)
at org.mockito.internal.creation.cglib.ClassImposterizer.setConstructorsAccessible(ClassImposterizer.java:85)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:71)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:49)
at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59)
at org.mockito.Mockito.mock(Mockito.java:1285)
at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:33)
at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:16)
at org.mockito.internal.configuration.DefaultAnnotationEngine.createMockFor(DefaultAnnotationEngine.java:43)
at org.mockito.internal.configuration.DefaultAnnotationEngine.process(DefaultAnnotationEngine.java:66)
at org.mockito.internal.configuration.InjectingAnnotationEngine.processIndependentAnnotations(InjectingAnnotationEngine.java:71)
at org.mockito.internal.configuration.InjectingAnnotationEngine.process(InjectingAnnotationEngine.java:55)
at org.mockito.MockitoAnnotations.initMocks(MockitoAnnotations.java:108)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl$1.withBefores(JUnit45AndHigherRunnerImpl.java:27)
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.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:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.ClassNotFoundException: android.content.Context
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 39 more


Test ignored.
Process finished with exit code 255

I understand that I can't use the android framework on unit tests, but I didn't expect the real constructor of OrderRepository to be called. Being a mocked object, I thought it would just be replaced with some other dumb/mock implementation.

My question is: am I missing anything or the constructors of objects annotated with @Mock are indeed supposed to be called? If so, how can I mock a dependency which expects a Context as a param to its constructor?

Mateus Gondim
  • 5,362
  • 6
  • 31
  • 51

2 Answers2

2

Mockito does not call the constructor; you can put logging in a simple class's constructor to test this. However, under the hood Mockito sets the accessibility of all constructors to public before creating the class.

You need to have the classpath of your unit tests contain the android libraries so when the constructors are loaded by Mockito it can see Context even though it doesn't actually use them.

EDIT: If you're curious what it's doing under the hood there's an example with explanation in this answer that shows what Mockito's libraries are doing in the last 5 lines of your stacktrace.

Community
  • 1
  • 1
Daniel Bickler
  • 1,119
  • 13
  • 29
1

OrderRepository has a single constructor, which receives an Android Context as a param. This is causing the unit test to fail with the following stack trace:

java.lang.NoClassDefFoundError: android/content/Context

No!

The error message tells that you have a problem with your classpath.

Community
  • 1
  • 1
Timothy Truckle
  • 15,071
  • 2
  • 27
  • 51