如何处理“任何其他价值"?与 Mockito?

2022-01-14 00:00:00 mockito java

我有一个接口 Foo 和方法 int Foo.bar(int) 我想用 Mockito 模拟.如果我传入 1,我希望模拟方法返回 99,但所有其他值都会引发异常.我可以这样做吗?

I have an interface Foo with method int Foo.bar(int) that I want to mock with Mockito. I want the mocked method to return 99 if I pass in 1, but all other values will throw an exception. Can I do this?

final Foo foo = mock(Foo.class);
when(foo.bar(1)).thenReturn(99);
when(foo.bar(anyInt())).thenThrow(new IllegalArgumentException());

换句话说,1 会优先于 anyInt() 吗?我不希望它为 1 抛出异常.docs 说对于多个定义,最后一个定义更重要,但我不知道这是否意味着相同的论点.如果在这里适用,我需要先定义通配符 anyInt() 吗?或者两者是否有任何关系,因为其中一个是匹配器,另一个只是一个值?

In other words, will the 1 take precedence over anyInt()? I wouldn't want it throwing an exception for 1. The docs say that for multiple definitions the last definition is more important, but I couldn't tell if that meant for identical arguments or not. If it applies here, would I need to define the wildcard anyInt() first? Or do the two even have any relation, as one of them is a matcher and the other is just a value?

推荐答案

您有两个选择:匹配除一个之外的任何值",以及覆盖存根.(我想您也可以将 Answer 用于复杂的自定义行为,但对于这种情况来说,这太过分了.)

You have two options: Matching "any value but one", and overriding stubbing. (I suppose you could also use an Answer for complex custom behavior, but that's overkill for situations like this one.)

Mockito 的 AdditionalMatchers 类提供了许多有用的匹配器,包括操作符,例如 不是.这将允许您为除特定值(或表达式)之外的所有值设置行为.

Mockito's AdditionalMatchers class offers a number of useful matchers, including operators such as not. This would allow you to set behavior for all values except for a specific value (or expression).

when(foo.bar(1)).thenReturn(99);
when(foo.bar(not(eq(1)))).thenThrow(new IllegalArgumentException());

请注意,运算符必须与匹配器而不是值一起使用,由于 Mockito 的 Matchers.eq 作为显式 equals 匹配器"https://stackoverflow.com/a/22822514/1426891">参数匹配器堆栈:

Be careful to note that operators must be used with matchers instead of values, possibly requiring Matchers.eq as an explicit equals matcher, due to Mockito's argument matcher stack:

/* BAD */  when(foo.bar(not(  1  ))).thenThrow(new IllegalArgumentException());
/* GOOD */ when(foo.bar(not(eq(1)))).thenThrow(new IllegalArgumentException());

覆盖存根

对于存根,最后定义的匹配链获胜.这允许您在 @Before 方法中设置一般测试夹具行为,并根据需要在单个测试用例中覆盖它,但也意味着在您的存根调用中顺序很重要.

Overriding stubbing

For stubbing, the last-defined matching chain wins. This allows you to set up general test fixture behavior in a @Before method and override it in individual test cases if you wish, but also implies that order matters in your stubbing calls.

when(foo.baz(anyInt())).thenReturn("A", "B");  /* or .thenReturn("A").thenReturn("B"); */
when(foo.baz(9)).thenReturn("X", "Y");

foo.baz(6); /* "A" because anyInt() is the last-defined chain */
foo.baz(7); /* "B" as the next return value of the first chain */
foo.baz(8); /* "B" as Mockito repeats the final chain action forever */

foo.baz(9); /* "X" because the second chain matches for the value 9 */
foo.baz(9); /* "Y" forever because the second chain action still matches */

因此,您永远不会看到问题中列出的顺序中的两个存根,因为如果一般匹配紧跟特定匹配,则永远不会使用特定匹配(也可能被删除).

Consequently, you should never see the two stubs in the order listed in the question, because if a general match immediately follows a specific match then the specific match will never be used (and may as well be deleted).

请注意,在覆盖间谍或危险的存根行为时,您有时需要将语法更改为 doAnswer.Mockito 知道不计算对 when 的调用以进行验证或沿 thenVerb 链前进,但异常仍可能导致您的测试失败.

Beware that you'll sometimes need to change syntax to doAnswer when overriding spies or dangerous stubbed behavior. Mockito knows not to count calls to when for verification or for advancing along thenVerb chains, but exceptions could still cause your test to fail.

/* BAD: the call to foo.bar(1) will throw before Mockito has a chance to stub it! */
when(foo.bar(anyInt())).thenThrow(new IllegalArgumentException());
when(foo.bar(1)).thenReturn(99);

/* GOOD: Mockito has a chance to deactivate behavior during stubbing. */
when(foo.bar(anyInt())).thenThrow(new IllegalArgumentException());
doReturn(99).when(foo).bar(1);

相关文章