Is there a way in JMockit to call the original met

2020-02-11 05:21发布

问题:

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

回答1:

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



回答2:

In more recent versions of JMockit, Invocation.proceed() can be called from within a MockUp implementation. See Faking: Proceeding into the real implementation.

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


回答3:

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