New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

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

Comments

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 added a commit that referenced this issue Jan 17, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment