在JMockit中,是否有一种方法可以从模拟方法中调用原始方法?

8
在我的模拟类中,我正在模拟方法foo()。对于某些测试用例,我希望foo()的模拟实现返回一个特殊值。对于其他测试用例,我想使用foo()的真实实现。我在我的模拟类中定义了一个布尔变量,以便在模拟方法中确定我是否要返回特殊值或使用“真实”方法。问题是,我似乎无法弄清楚如何从模拟方法调用真实方法。
我发现您可以在模拟对象中定义一个名为“it”的特殊成员(类型为被模拟的对象)。这使您可以从模拟实现中引用真实类。因此,我的计划是,如果我需要调用foo()的“真实”实现,则模拟方法将调用it.foo()。然而,这并不起作用,因为调用it.foo()只会再次调用模拟版本,而不是真实版本,因此我最终得到无限递归。
有没有办法让这个工作?
编辑:通过代码示例可能更清楚,这是我的当前模拟方法实现:
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: 另外,对于大多数测试用例,我想要模拟实现。因此,我的初始尝试是仅在需要模拟对象的测试用例中调用Mockit.redefineMethods()。但这并不起作用 - 当我尝试时,似乎只能在设置/拆卸中这样做...我的模拟实现从未被调用。
解决方案注释:
起初,我认为给出的答案没有起作用,但在进一步尝试后,问题似乎是我混合使用了JMockit“核心”方法和“注释”驱动方法。显然,在使用注释时,您需要使用Mockit.setupMocks而不是Mockit.redefineMethods()。这就是最终起作用的内容:
@Before 
public void setUp() throws Exception
{
    Mockit.setUpMocks(MyMockClass.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();
        }
    }
}
4个回答

13

在JMockit的较新版本中,可以在MockUp实现中调用Invocation.proceed()。请参阅访问调用上下文

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

这个答案中的链接已经失效了。JMockIt的新链接为:(https://jmockit.github.io/tutorial/Faking.html#proceed) - Endle_Zhenbo

8

我一定是漏掉了什么。从文档上看,它听起来很有前途,但无论我使用reentrent=true还是false,我得到的行为都与以前相同。此外,文档说“默认情况下,不允许这样的调用,因为它们会导致无限递归……”,所以我感到困惑。 - Eric Asberry
经过进一步的审查,我发现了我所缺少的 :) - Eric Asberry
我更新了JavaDoc链接并添加了一个示例链接。对于这个回答给予+1。 - Tobias Sarnow
5
链接已经失效,因为已经超过6年时间。 - demongolem
我认为值得注意的是,这个答案只在问题被回答时才是有效的。在较新版本的JMockit中,这已经被弃用了。在我看来,Trevor Robinson给出的答案现在是最好的答案。如果您使用旧版本的JMockit,则此答案仍然有效。 - searchengine27

1

不要随意抛入一个模拟对象,你还可以子类化想要测试的对象并重写应该返回特殊值的方法。

例如:

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

//use toTest in test

通过在测试中保留这个定义,对于其他人来说,被“mocked”的方法也变得清晰明了。


谢谢您的建议!不幸的是,我不能使用那种方法,因为我正在模拟的类实际上并不是由测试用例直接创建的。它是由被测试的对象创建的。 - Eric Asberry

0

如果还有人感兴趣,可以在Jmockit文档中找到支持。

因此,以这个问题为例,可以按以下方式实现:

SomeClass someClass = new SomeClass("fakevalue");

new Expectations(someClass){{
    someClass.foo();
    result = <mock>;
}};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接