如何使用 Mockito 验证重载方法的调用次数?

如何使用 Mockito 检查 bar(Alpha, Baz) 是否调用了 bar(Xray, Baz) - 鉴于我的 MCVE 类 Foo:

How do I check if bar(Alpha, Baz) called bar(Xray, Baz) using Mockito - without actually calling the later, given my MCVE class Foo:

public class Foo {
    public String bar(Xray xray, Baz baz) {
        return "Xray";
    }

    public String bar(Zulu zulu, Baz baz) {
        return "Zulu";
    }

    public String bar(Alpha alpha, Baz baz) {
        if(alpha.get() instanceof Xray) {
            return bar((Xray)alpha.get(), baz);
        } else if(alpha.get() instanceof Zulu) {
            return bar((Zulu)alpha.get(), baz);
        } else {
            return null;
        }
    }
}

目前,我大致有以下 Mockito 增强的 JUnit 测试:

Currently, I have roughly the following Mockito-enhanced JUnit test:

@Test
public void testBar_callsBarWithXray() {
    Baz baz = new Baz(); //POJOs
    Alpha alpha = new Alpha();
    alpha.set(new Xray());

    Foo foo = new Foo();
    Foo stub = spy(foo); // Spying on Foo, as I want to call the real bar(Alpha, Baz)
    // Preventing bar(Xray, Baz) from being called by providing behavior/stub
    when(stub.bar(any(Xray.class), any(Baz.class))).thenReturn("ok");
    // Calling the real "parent" method
    stub.bar(alpha, baz);
    // Testing if bar(Xray, Baz) was called by bar(Alpha, Baz)
    verify(stub, times(1)).bar(any(Xray.class), any(Baz.class));
}

它失败了:

org.mockito.exceptions.verification.TooManyActualInvocations:
foo.bar(
    <any>,
    <any> ); 
Wanted 1 time:
-> at FooTest.testBar_callsBarWithXray(FooTest.java:14) 
But was 2 times. Undesired invocation:
-> at FooTest.testBar_callsBarWithXray(FooTest.java:12)

我想这是因为 any(Class.class) 匹配任何东西,并且不进行类型检查......所以,从 Mockito 的角度来看,我真的在调用 bar() 两次:在 FooFooTest 中(第 12 行).

I suppose it's because any(Class.class) matches anything, and doesn't do type checking... So, from Mockito's point of view, I'm really calling bar() twice: in Foo and in FooTest (on line 12).

我必须做什么才能让测试做我真正想要的:确保在我调用 bar(Alpha, baz)bar(Xray, Baz)>(同时拦截对bar(Xray, Baz)的调用)?

What do I have to do to make the test do what I actually want: ensure bar(Xray, Baz) was called when I call bar(Alpha, baz) (while at the same time intercepting the call to bar(Xray, Baz))?

使用 eq() 匹配器 (when(stub.bar(eq(Xray.class), any(Baz.class))...) 会导致编译错误,Cannot resolve method 'bar(Class<Xray>, Baz)' - 但是,我可能不应该那样使用它(eq(xray)可能会更喜欢)...

Using the eq() Matcher (when(stub.bar(eq(Xray.class), any(Baz.class))...) causes a compilation error, Cannot resolve method 'bar(Class<Xray>, Baz)' - but then, I probably shouldn't use it that way anyway (eq(xray) would probably be more like it)...

另外,在相关说明中,如果我尝试使 bar(Alpha, Baz) private 的重载,我会收到 FooTest 的编译错误,声明:

Also, on a related note, if I try to make the overloads of bar(Alpha, Baz) private, I get a compilation error for FooTest, stating:

错误:(10, 12) java: bar(Xray,Baz) 在 Foo 中具有私有访问权限

Error:(10, 12) java: bar(Xray,Baz) has private access in Foo

有没有办法使用 PowerMockito 来解决这个问题?如何?(显然,我只想计算对 bar(Xray, Baz) 的调用 - 计算对 bar() 的所有重载的所有调用是不可能的......)

Is there a way to get round that using PowerMockito? How? (Obviously, I only want to count the calls to bar(Xray, Baz) - counting all calls to all overloads of bar() is out of the question...)

只需在测试的第 10 行和第 14 行使用 eq(xray) 而不是 any(Xray.class) 就可以解决问题 - 但我并不感兴趣在 what 中(特定)Xray 对象作为参数传入 bar(),只要它是 any Xray对象...

Just using eq(xray) instead of any(Xray.class) on lines 10 and 14 of the test does the trick - but I'm not really interested in what (specific) Xray object is passed in as an argument to bar(), as long as it's any Xray object...

更新:我为PowerMockito 和 private 方法发布了一个单独的问题" 讨论的一部分,并弄清楚了如何使 public 方法工作;参看.我的在下方评论.

Update: I've posted a separate question for the "PowerMockito and private methods" part of the discussion, and figured out how to make this work for public methods; cf. my comment below.

推荐答案

你不需要这样做.

这让你发疯的原因是你没有按照预期的方式使用 Mockito.它可以这样使用,但不应该这样.

You don't need to do this.

The reason this is driving you nuts is that you aren't using Mockito the way it is intended to be used. It can be used this way, but it should not be.

您无需测试是否调用了正确的方法,您需要测试您的类的行为是否正确.这是什么意思?

You don't need to test that the right method was called, you need to test that the behavior of your class is correct. What does that mean?

  1. 您要检查是否返回了 "Xray".

String result = foo.bar(alpha, baz);
assertEquals("Xray", result);

  • 您想检查是否调用了 Alpha.get.如果有的话,这意味着 Alpha 应该是存根,而不是 Foo.

  • You want to check that Alpha.get was called. If anything, this means that Alpha should be the stub, not Foo.

    Alpha alpha = spy(new Alpha());
    String result = foo.bar(alpha, baz);
    verify(alpha).get();
    

  • 底线是:您的系统处于被测状态,在本例中为 Foo.该类应该是一个真正的类.您的模拟和存根应该是与 Foo 交互的其他对象;验证交互做正确的事情.

    The bottom line is this: You have your System Under Test, in this case Foo. That class should be a real class. Your mocks and stubs should be the other objects that interact with Foo; verify that the interactions do the correct things.

    换句话说,你不应该测试 Foo 的工作原理,你应该测试那个 Foo 做它应该做的;结果是正确的.

    In other words, you shouldn't be testing how Foo works, you should be testing that Foo does what it's supposed to do; that the results are correct.

    相关文章