views:

1067

answers:

3

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();
        }
    }
}
A: 

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'.

EricBouwers
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
+2  A: 

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

See https://jmockit.dev.java.net/javadoc/mockit/Mock.html

I haven't tested this though..

Kris Pruden
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
After further review, I found what I was missing :)
Eric Asberry
A: 

Sounds like a better plan would be to split your tests cases between the ones that use mocks and the ones that use the real thing. Doing it inside just looks too brittle and complicated.

Steve Freeman
Why is this a bad comment? Please don't hide.
Steve Freeman