8

In my mock class, I'm mocking method foo(). For some test cases, I want the mock implementation of foo() to return a special value. For other test cases, I want to use the real implementation of foo(). I have a boolean defined in my mock class so that I can determine in the mock method whether I want to return the special value, or use the "real" method. The problem is, I can't seem to figure out how to call the real method from the mocked method.

I found that you can define a special member within the mock object named "it" (with type of the object being mocked). This allows you to reference the real class from the mock implementation. So, my plan was, if I needed to invoke the "real" implementation of foo(), the mock method would call it.foo(). However, this doesn't work, because calling it.foo() just calls the mock version again, not the real version, so I end up with infinite recursion.

Is there some way to make this work?

EDIT: it might be clearer with a code example, here's what my current mocked method implementation looks like:

private RealClass it;
...
public SomeClass foo() {
    if(fakeIt) {
        return new SomeClass("fakevalue");
    } else {
        // doesn't work, just keeps calling the mock foo
        // in infinite recursion
        return it.foo();
    }
}

EDIT 2: Also, for most of my test cases I do NOT want the mock implementation. So my initial attempt at this was to only call Mockit.redefineMethods() within those test cases where I needed the mock object. But this didn't work - it seems you can only do this within setup/teardown ... my mock implementation never got called when I tried that.

NOTES ON SOLUTION:

At first I didn't think the answer given worked, but after playing with it some more, it seems the problem is that I was mixing JMockit "core" methods with the "annotation" driven methods. Apparently when using the annotation you need to use Mockit.setupMocks, not Mockit.redefineMethods(). This is what finally worked:

@Before 
public void setUp() throws Exception
{
    Mockit.setUpMocks(MyMockClass.class);
}

Then, for the mock class:

@MockClass(realClass = RealClass.class)
public static class MyMockClass {
    private static boolean fakeIt = false;
    private RealClass it;

    @Mock(reentrant = true)
    public SomeClass foo() {
        if(fakeIt) {
            return new SomeClass("fakevalue");
        } else {
            return it.foo();
        }
    }
}
Eric Asberry
  • 612
  • 1
  • 5
  • 11

4 Answers4

13

In more recent versions of JMockit, Invocation.proceed() can be called from within a MockUp implementation. See Accessing the invocation context.

public class MyMockClass extends MockUp<RealClass> {

    private static boolean fakeIt = false;

    @Mock
    public SomeClass foo(Invocation inv) {
        if (fakeIt) {
            return new SomeClass("fakevalue");
        } else {
            return inv.proceed();
        }
    }
}
Nathan
  • 8,093
  • 8
  • 50
  • 76
Trevor Robinson
  • 15,694
  • 5
  • 73
  • 72
  • The link in this answer is a broken link. New link for JMockIt: (https://jmockit.github.io/tutorial/Faking.html#proceed) – Endle_Zhenbo Nov 25 '20 at 20:17
8

I think you can do this with the @Mock annotation. From the docs, @Mock(reentrant=true) on your mock class should do it.

See http://jmockit.googlecode.com/svn/trunk/www/javadoc/mockit/Mock.html
For an example look here http://jmockit.googlecode.com/svn/trunk/www/tutorial/StateBasedTesting.html#reentrant

I haven't tested this though..

Tobias Sarnow
  • 1,076
  • 2
  • 12
  • 40
Kris Pruden
  • 3,280
  • 4
  • 25
  • 30
  • I must be missing something. From the doc it does sound promising, but it doesn't seem to matter whether I use reentrent=true or false, I get the same behavior as before. Also the doc says "By default, such calls are not allowed because they lead to infinite recursion ..." so I'm confused. – Eric Asberry Dec 11 '08 at 15:38
  • After further review, I found what I was missing :) – Eric Asberry Dec 11 '08 at 16:12
  • I updated the JavaDoc link and added an example link. +1 For the answer. – Tobias Sarnow Nov 08 '11 at 15:04
  • 5
    Links are broken as it has been over 6 years. – demongolem Feb 03 '15 at 16:26
  • I think it's worth noting this answer was only valid back when the question was answered. This has since been deprecated in the JMockit API in more recent versions of JMockit. The answer given by Trevor Robinson is now the best answer, imo. This answer is only still valid if you're using an old version of JMockit. – searchengine27 Dec 19 '16 at 19:10
1

Instead of throwing in a mock object you could also subclass the object you want to test and override the methods that should return special values.

For example:

RealClass toTest = new RealClass(){
      public String foo(){
          return "special value";
      }
}

//use toTest in test

By keeping this definition within your test it is also clear for others which methods are being 'mocked'.

ebo
  • 2,717
  • 1
  • 27
  • 22
  • Thanks for the suggestion! Unfortuntaely, I can't use that approach because the class that I'm mocking isn't actually being created directly by the test case. It's been created by the object which is under test. – Eric Asberry Dec 10 '08 at 20:25
0

Whomever is still interested, there is a support for that in Jmockit docs

So given this question as an example, it can be achieved as follows:

SomeClass someClass = new SomeClass("fakevalue");

new Expectations(someClass){{
    someClass.foo();
    result = <mock>;
}};
Pavel
  • 1,627
  • 15
  • 12