'Unexpected invocation’ while mocking self returning class via @Mocked and StrictExpectations #248

Closed
yeskov opened this Issue Jan 14, 2016 · 0 comments

Projects

None yet

2 participants

@yeskov
yeskov commented Jan 14, 2016

Hello.
I have noticed strange issues while mocking classes which returns self as method result value (like builder). Here is an example:

public SelfReturningClass doSmthAndReturnSelf(int param){
        System.out.print(param);
        return this;
    }
}

public class TestCascade {

    @Mocked
    private SelfReturningClass selfReturningClass;

    @Test
    public void testFailed(){

        SelfReturningClass anotherInstance = new SelfReturningClass();

        new StrictExpectations() {{
            selfReturningClass.doSmthAndReturnSelf(1);
            selfReturningClass.doSmthAndReturnSelf(2);
        }};

        System.out.println("Injected @Mocked object " + selfReturningClass);
        System.out.println("AnotherInstance " + anotherInstance);

        Object ignoredReturnValue1 = anotherInstance.doSmthAndReturnSelf(1);
        System.out.println("ignoredReturnValue1 " + ignoredReturnValue1);
        Object ignoredReturnValue2 = anotherInstance.doSmthAndReturnSelf(2);
        System.out.println("ignoredReturnValue2 " + ignoredReturnValue2);

    }
}

Running this test gives me :

Injected @Mocked object SelfReturningClass@1d251891
AnotherInstance SelfReturningClass@7c30a502
ignoredReturnValue1 SelfReturningClass@1d251891

mockit.internal.UnexpectedInvocation: Unexpected invocation of:
SelfReturningClass#doSmthAndReturnSelf(int)
   with arguments: 2
   on instance: SelfReturningClass@7c30a502
when was expecting an invocation of:
SelfReturningClass#doSmthAndReturnSelf(int)
   with arguments: 2
   on mock instance: SelfReturningClass@1d251891
    at SelfReturningClass.doSmthAndReturnSelf(SelfReturningClass.java)
    at TestCascade.testFailed(TestCascade.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: Missing invocation
    at SelfReturningClass.doSmthAndReturnSelf(SelfReturningClass.java)
    at TestCascade$1.<init>(TestCascade.java:17)
    at TestCascade.testFailed(TestCascade.java:15)
    ... 8 more

First invocation is successful.
For second invocation I get "Unexpected invocation" .
Method signature matches exactly. Instances are different - but i am using @Mocked injection type, so i must mock all instances of this class.

I captured returned value to variable ‘ignoredReturnValue1’, for debugging proposes in this test, because i thought there are issue with cascade mocking. But every thing seems fine: returned value equals to «Injected @Mocked object».

I found workaround how to get test working :

@Test
public void testWorking(){

    SelfReturningClass anotherInstance = new SelfReturningClass();

    new StrictExpectations() {{
        selfReturningClass.doSmthAndReturnSelf(1); result = selfReturningClass;
        selfReturningClass.doSmthAndReturnSelf(2);
    }};

    System.out.println("Injected @Mocked object " + selfReturningClass);
    System.out.println("AnotherInstance " + anotherInstance);

    Object ignoredReturnValue1 = anotherInstance.doSmthAndReturnSelf(1);
    System.out.println("ignoredReturnValue1 " + ignoredReturnValue1);
    Object ignoredReturnValue2 = anotherInstance.doSmthAndReturnSelf(2);
    System.out.println("ignoredReturnValue2 " + ignoredReturnValue2);

}

When i explicitly return mocked object as result then subsequent invocation works fine:

Injected @Mocked object SelfReturningClass@1d251891
AnotherInstance SelfReturningClass@7c30a502
ignoredReturnValue1 SelfReturningClass@1d251891
ignoredReturnValue2 SelfReturningClass@1d251891

But in production test code i really ignore returning value. That’s why i do not specify any return value in this expectation originally.

So do i miss something in this combination:
@Mocked + several invocations in StrictExpectations + self returning objects?
Should i always use result statement in expectation block when method is non void , or i can omit result?
Is it a bug behaviour of JMockit?

I have attached working example, so you can reproduce issue. (tried on jdk 1.8 and 1.7)

jmockit_cascade_issue.zip

@rliesenfeld rliesenfeld added the bug label Jan 14, 2016
@rliesenfeld rliesenfeld self-assigned this Jan 14, 2016
@rliesenfeld rliesenfeld added a commit that referenced this issue Jan 17, 2016
@rliesenfeld rliesenfeld Fixed cascading bug where regular @Mocked instance was used as if it …
…was injectable; closes issue #248.
2ab66c0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment