JMockit 有没有办法从模拟方法调用原始方法?

发布于 2024-07-10 06:39:34 字数 1515 浏览 6 评论 0原文

在我的模拟类中,我正在模拟方法 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();
    }
}

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

撞了怀 2024-07-17 06:39:34

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

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

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();
        }
    }
}
仲春光 2024-07-17 06:39:34

我认为你可以使用 @Mock 注释来做到这一点。 从文档中,您的模拟类上的 @Mock(reentrant=true) 应该可以做到这一点。

请参阅 http://jmockit.googlecode.com/svn/trunk/ www/javadoc/mockit/Mock.html

有关示例,请参见此处 http://jmockit.googlecode .com/svn/trunk/www/tutorial/StateBasedTesting.html#reentrant

不过我还没有测试过这个..

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

殤城〤 2024-07-17 06:39:34

您还可以对要测试的对象进行子类化并重写应返回特殊值的方法,而不是抛出模拟对象。

例如:

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

//use toTest in test

通过在测试中保留此定义,其他人也可以清楚哪些方法正在被“模拟”。

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

好倦 2024-07-17 06:39:34

谁仍然感兴趣, Jmockit docs 中都有对此的支持

无论 以本题为例,可以这样实现:

SomeClass someClass = new SomeClass("fakevalue");

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

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>;
}};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文