0

These two unit tests are the same but the mocked object, utility, only receives a call in the first test. The method calling the mock, doTheThing(), runs as expected. Why doesn't the mocked object utility receive a call in both tests?

I have ran these tests

  • in Eclipse
  • with mvn command
  • with java command running JUnitCore class, with Junit, Easy Mock and required libraries on class path

I am using Easy Mock 4.0.2 and Junit 4.12.

ComponentTest class

import static org.easymock.EasyMock.anyString;
import static org.easymock.EasyMock.expect;

import org.easymock.EasyMockRunner;
import org.easymock.EasyMockSupport;
import org.easymock.Mock;
import org.easymock.TestSubject;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(EasyMockRunner.class)
public class ComponentTest extends EasyMockSupport {
    @TestSubject
    private Component component = new Component();
    @Mock
    private Utility utility;

    @Test
    public void testDoTheThing1() {
        Context.setUtility(utility);
        expect(utility.doSomethingWith(anyString())).andReturn(null);
        replayAll();
        component.doTheThing("aa");
        verifyAll();
        resetAll();
    }

    @Test
    public void testDoTheThing2() {
        Context.setUtility(utility);
        expect(utility.doSomethingWith(anyString())).andReturn(null);
        replayAll();
        component.doTheThing("aa");
        verifyAll();
        resetAll();
    }
}

Component class

public class Component implements Processor{
    public String doTheThing(String request) {
        return UTILITY.doSomethingWith(request);
    }
}

Processor interface

public interface Processor {
    Utility UTILITY = Context.getUtility();
}

Context class

public class Context {
    private static Utility utility;

    public static Utility getUtility(){
        return utility;
    }

    public static void setUtility(Utility utility) {
        Context.utility = utility;
    }
}

Utility class

public class Utility {
    public String doSomethingWith(String request) {
        return null;
    }
}

Junit output

There was 1 failure:
1) testDoTheThing2(ComponentTest)
java.lang.AssertionError: 
  Expectation failure on verify:
    Utility.doSomethingWith(<any>): expected: 1, actual: 0
    at org.easymock.internal.MocksControl.verify(MocksControl.java:242)
    at org.easymock.EasyMockSupport.verifyAll(EasyMockSupport.java:523)
    at ComponentTest.testDoTheThing2(ComponentTest.java:41)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    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.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 org.junit.runner.JUnitCore.runMain(JUnitCore.java:77)
    at org.junit.runner.JUnitCore.main(JUnitCore.java:36)
Progman
  • 16,827
  • 6
  • 33
  • 48
brendan
  • 384
  • 3
  • 10
  • This is very similar to [Why is my class not calling my mocked methods in unit test?](https://stackoverflow.com/q/74027324/112968) with the same underlying problem. – knittl Oct 20 '22 at 19:34

1 Answers1

2

From : https://docs.oracle.com/javase/tutorial/java/IandI/interfaceDef.html

All constant values defined in an interface are implicitly public, static, and final.

So whereas a new Utility mock is created when each test method is run, Processor.UTILITY is implicitly a static final constant, which will only hold its first assigned value - which would be the value from the first unit test.

So instead of @Mock Utility, you should instead use “static final Utility”, and work with that single instance across all the test methods.

Edit To Add: Note that before each individual test method is run, each class member annotated with @TestSubject and @Mock is re-created new per test, and those new mocks are injected into the new test subject. This minimises the carryover of state / behaviour from one test to another. Coincidentally, Mockito also does this.

racraman
  • 4,988
  • 1
  • 16
  • 16
  • Interesting, does @Mock create a new Utility per test? – brendan Sep 07 '19 at 22:12
  • Yes, the @ Mocks and the @ TestSubject. See http://easymock.org/user-guide.html “Annotations” section - the annotations replace the @ Before setup - which is the before-each-test-method. The idea is to make it as easy as possible to keep tests independent of each other as possible - “test2” should not pass or fail if “test1” was run or not. The easiest way of helping with that is to create a whole new set of objects for each test method run. – racraman Sep 07 '19 at 22:55
  • Could you say Mock, TestSubject is new per test in the answer? For me, that is the answer to, why doesn't the mocked object receive a call? I know the guide says the setUp method can be removed since all the initialization was done by the runner but I didn't know the initialization was per test like an @ Before method. Thanks very much @racraman – brendan Sep 07 '19 at 23:12
  • Yes, it doesn’t seem to get much attention, but is so important. I’ve edited the answer as you’ve suggested, hope it reads well :) – racraman Sep 08 '19 at 00:06
  • Great answer @racraman. After debugging, it seems to me a new instance of the whole test class is created per test. – brendan Sep 08 '19 at 00:15