我可以操纵模拟匹配器的顺序吗?



一些上下文

在设置模拟(when)或验证模拟上的调用(verify)时,Mockito要求您提供模拟方法所需的所有具体值,或者为所有值提供匹配器。无法混合这些样式。

when(mock.method(1, 2, 3));
when(mock.method(eq(1), eq(2), eq(3)));

我说的是第二种风格。

由于 Mockito 的工作方式,匹配器的调用顺序很重要。在内部,Mockito将在堆栈上注册匹配器,并在必要时按顺序执行它们。

我努力实现的目标

我想编写一些与 mockito 一起使用的测试实用程序。我希望这些实用程序方法将调用委托给模拟,插入一些默认匹配器,否则这些匹配器将是样板测试代码。

例如:

public String callOnMock(int argument2) {
return mock.call(eq(1), argument2, argThat(i -> i >= 3));
}

将像这样使用:

when(callOnMock(eq(2)).thenReturn("result");

问题所在

这是行不通的,因为 Mockito 以错误的顺序注册这些匹配器:

  1. eq(2)
  2. eq(1)
  3. argThat(i -> i >= 3)

虽然它应该是

  1. eq(1)
  2. eq(2)
  3. argThat(i -> i >= 3)

有没有办法让纵这些匹配器的注册顺序?

我现在org.mockito.AdditionalMatchers有操纵内部堆栈的方法,允许匹配器组合(andornot),所以至少在 Mockito 核心内部是可能的。

是否也可以显式弹出和推送匹配器?

使用Supplier

public String callOnMock(Supplier<Integer> argument2) {
return mock.call(eq(1), argument2.get(), argThat(i -> i >= 3));
}
when(callOnMock(() -> eq(2)).thenReturn("result");

试试这个:

public String callOnMock(int argument2) {
return mock.call(eq(1), eq(argument2), argThat(i -> i >= 3));
}

并像这样称呼它:

when(callOnMock(2)).thenReturn("result");

我认为有几种方法可以实现所需的行为。

1. 操纵堆栈上匹配器的顺序

这不是要走的路!

matcherStack似乎是Mockito的内部。
它们确实有一种从堆栈pullLocalizedMatchers的方法,以及一种将ArgumentMatcher推送到堆栈上的reportMatcher方法。这些可以通过以下方式访问

org.mockito.internal.progress.ThreadSafeMockingProgress
.mockingProgress()
.getArgumentMatcherStorage()

所以理论上你可以选择这条路,但解决方案会很脆弱,因为你弄乱了 Mockito 的内部结构。它们可能会在 Mockito 的后续版本中更改,恕不另行通知。

幸运的是,有几种选择。

2. 首先控制匹配器的注册顺序

使用 Java 8Supplier函数接口(这与@ToYonos给出的答案相对应)

当你调用创建匹配器的方法时,Mockito会自动注册匹配器(eqargThatanyisNotNull,...)。但是,您可以通过为每个匹配器传递Supplier来延迟调用这些方法。然后,便利方法控制它执行这些供应商的顺序。

public String callOnMock(Supplier<Integer> argument2) {
return mock.call(eq(1), argument2.get(), argThat(i -> i >= 3));
}
when(callOnMock(() -> eq(2))).thenReturn("result");

使用它看起来与正常的 Mockito 风格略有不同。

由于同样的问题,如果您为那些使用/聚合其他匹配器的供应商提供方便的方法,则需要特别小心。

callOnMock(() -> AdditionalMatchers.and(isNotNull(), eq(2)))

将起作用,
但这不会:

public Supplier<Integer> and(int matcher1, int matcher2){
return () -> AdditionalMatchers.and(matcher1, matcher2);
}
callOnMock(and(isNotNull(), eq(2)))

这会给方法的用户带来一些责任。他们必须确保没有一个匹配者被意外调用。

3. 控制模拟期望匹配器的顺序

将模拟调用委托给不同的模拟对象可以让你控制参数的顺序。
您必须定义一个接口,该接口期望匹配器按照您的便利方法接收它们的顺序,将便利方法添加的匹配器放在末尾。
必须针对该委托接口做出期望。

public interface MockDelegate {
String call(Integer i1, Integer i0, Integer i2);
}
@Mock
private MockDelegate delegate;
@Before
public void setUp() {
when(mock.call(any(), any(), any()))
.thenAnswer(invocation -> delegate.call(
invocation.getArgument(1), // this delegates the call
invocation.getArgument(0), // but flips the first two arguments
invocation.getArgument(2)
));
}
public String callOnMock(int argument2) {
return delegate.call(argument2, eq(1), argThat(i -> i >= 3));
}

这可以与普通的 Mockito 风格匹配器一起使用:

when(callOnMock(eq(2))).thenReturn("result");

相关内容

  • 没有找到相关文章

最新更新