Problem using @Spy: behaves differently in JUnit & JUnit Plugin tests (eclipse, maven)

341 views
Skip to first unread message

Geert Vermeiren

unread,
Jan 7, 2015, 3:28:42 AM1/7/15
to moc...@googlegroups.com
Hello all,

I've been using Mockito for a while now on various projects, and I'm pretty happy with it ;-)
However, on my current project, I have run into a strange problem. Mainly strange because I'm not sure why it is not working as I expected..

In short: the test below is successful when running it as a pure JUnit test in Eclipse (or command-line maven).
However, when I run the same test as a JUnit Plugin test (Eclipse) or in our maven setup (tycho etc..), it fails...

I have been able to isolate the problem in some very simple classes, which I present below:

The class to be tested:

public class Calculator {

 
int calculatedValue = 0;

 
public void calculateNewValue(final int input) {
    calculatedValue
= externalConfigValue() * input;
 
}

 
int externalConfigValue() {
   
return ExternalAPI.getConfigValue();
 
}

}

Since mocking of the ExternalAPI static calls is not possible (read: feasible in our project), I have moved the call into a method.
Using the @Spy annotation, I'll mock the call to that method (to prevent the ExternalAPI to be called during the test).

The test-class:

import static org.mockito.Mockito.*;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;

public class CalculatorTest {

 
@Spy
 
Calculator calculator;

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

 
@Test
 
public void testCalculation() {
   
//prevent static call
    doReturn
(7).when(calculator).externalConfigValue();

    calculator
.calculateNewValue(6);

   
Assert.assertEquals(42, calculator.getCalculatedValue());
 
}
}

Running the test

When the above test-class is run as a regular JUnit test, the test succeeds.
i.e. using other values in the doReturn(..) preparation, and different valueses as input to calculateNewValue(..) produces expected results and behaviour.

The problem: when this test is run as a JUnit Plugin test in Eclipse, or as a test in a maven build using Tycho et al., the following exception occurs (making the test fail of course):

org.mockito.exceptions.base.MockitoException:
'calculateNewValue' is a *void method* and it *cannot* be stubbed with a *return value*!
Voids are usually stubbed with Throwables:
    doThrow
(exception).when(mock).someVoidMethod();
***
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. The method you are trying to stub is *overloaded*. Make sure you are calling the right overloaded version.
2. Somewhere in your test you are stubbing *final methods*. Sorry, Mockito does not verify/stub final methods.
3. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
   
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.

    at org
.woozong.CalculatorTest.testCalculation(CalculatorTest.java:25)
    at sun
.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun
.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun
.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java
.lang.reflect.Method.invoke(Method.java:606)
    at org
.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org
.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org
.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org
.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org
.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org
.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
    at org
.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
    at org
.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org
.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org
.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org
.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org
.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org
.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org
.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org
.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org
.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org
.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org
.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org
.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org
.eclipse.pde.internal.junit.runtime.RemotePluginTestRunner.main(RemotePluginTestRunner.java:62)
    at org
.eclipse.pde.internal.junit.runtime.CoreTestApplication.run(CoreTestApplication.java:23)
    at sun
.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun
.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun
.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java
.lang.reflect.Method.invoke(Method.java:606)
    at org
.eclipse.equinox.internal.app.EclipseAppContainer.callMethodWithException(EclipseAppContainer.java:587)
    at org
.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:198)
    at org
.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
    at org
.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
    at org
.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:344)
    at org
.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
    at sun
.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun
.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun
.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java
.lang.reflect.Method.invoke(Method.java:606)
    at org
.eclipse.equinox.launcher.Main.invokeFramework(Main.java:622)
    at org
.eclipse.equinox.launcher.Main.basicRun(Main.java:577)
    at org
.eclipse.equinox.launcher.Main.run(Main.java:1410)
    at org
.eclipse.equinox.launcher.Main.main(Main.java:1386)

Does anyone have any idea why this works properly as a simple JUnit, and not as a JUnit Plugin (or mvn/tycho/surefire) test ?

Kind regards,

Geert

Message has been deleted

Geert Vermeiren

unread,
Jan 7, 2015, 5:35:14 AM1/7/15
to moc...@googlegroups.com
Just to be complete, I'm using Mockito 1.9.0 on Eclipse Indigo platform.

Kind regards,

Geert

Hemant Kumar

unread,
Aug 19, 2015, 12:25:14 PM8/19/15
to mockito
Hi Geert,

Even I am facing the same issue while using JUnit plugin test. Please post if you could find any solution for this.

Regards,
Hemant

Geert Vermeiren

unread,
Aug 20, 2015, 8:36:07 AM8/20/15
to mockito
Hello Hemant,

Unfortunately I didn't find any solution within the available timebox, and so we moved on to a completely different solution involving eclipse/osgi services which can then be mocked more easily (i.e. the static methods on ExternalAPI as in the sample I gave, have disappeared).
Mind you, I never got any answers here.

Kind regards,

Geert

Brice Dutheil

unread,
Aug 22, 2015, 2:16:56 PM8/22/15
to moc...@googlegroups.com
Hi

I'm not familiar with Tycho. And I cannot reproduce the issue here. I cannot really help on your issue. There have been issues in the past with Eclipse. I don't remember the changelog, but 1.9.0 is pretty old, try 1.9.5 or 1.10.x. Could be an issue with Eclipse or maven plugin version used on this project too, it the past for example surefire introduced a bug with their classloader that broke mockito.

Anyway it seems that mockito sees another method being invoked here, is there anything in your toolchain that manipulate bytecode, coverage tool, aspectj, etc...?


-- Brice

--
You received this message because you are subscribed to the Google Groups "mockito" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mockito+u...@googlegroups.com.
To post to this group, send email to moc...@googlegroups.com.
Visit this group at http://groups.google.com/group/mockito.
To view this discussion on the web visit https://groups.google.com/d/msgid/mockito/8b4736e4-4bf2-4f22-9d66-dcfb24fa6b0e%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages