我可以操纵 mockito 匹配器的顺序吗?
Can I manipulate the order of mockito matchers?
一些上下文
在模拟上设置模拟 (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 以错误的顺序注册了这些匹配器:
eq(2)
eq(1)
argThat(i -> i >= 3)
应该是
eq(1)
eq(2)
argThat(i -> i >= 3)
有没有办法让我操纵这些匹配器的注册顺序?
我现在 org.mockito.AdditionalMatchers
有操作内部堆栈的方法允许匹配器组合(and
、or
、not
)所以至少在内部Mockito 核心是可能的。
是否也可以显式弹出和推送匹配器?
试试这个:
public String callOnMock(int argument2) {
return mock.call(eq(1), eq(argument2), argThat(i -> i >= 3));
}
并这样称呼它:
when(callOnMock(2)).thenReturn("result");
使用 Supplier
:
public String callOnMock(Supplier<Integer> argument2) {
return mock.call(eq(1), argument2.get(), argThat(i -> i >= 3));
}
when(callOnMock(() -> eq(2)).thenReturn("result");
我认为有几种方法可以实现所需的行为。
1。操纵堆栈上匹配器的顺序
This is not the way to go!
matcherStack
似乎是 Mockito 内部的。
他们确实有一个从堆栈 pullLocalizedMatchers
的方法和一个 reportMatcher
将 ArgumentMatcher
压入堆栈的方法。这些可以通过
访问
org.mockito.internal.progress.ThreadSafeMockingProgress
.mockingProgress()
.getArgumentMatcherStorage()
所以理论上你可以选择这条路,但解决方案会很脆弱,因为你会弄乱 Mockito 的内部结构。它们可能会在 Mockito 的后续版本中更改,恕不另行通知。
幸运的是有几个选择。
2。控制匹配器首先注册的顺序
使用Java8Supplier
功能接口(对应@ToYonos给出的)
当您调用创建匹配器的方法时,Mockito 会自动注册匹配器(eq
、argThat
、any
、isNotNull
、...)。但是您可以通过为每个匹配器传递 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 风格有点不同。
Special care needs to be taken if you offer convenience methods for those suppliers that use/aggregate other matchers, because of the same problem.
callOnMock(() -> AdditionalMatchers.and(isNotNull(), eq(2)))
will work,
but this will not:
public Supplier<Integer> and(int matcher1, int matcher2){
return () -> AdditionalMatchers.and(matcher1, matcher2);
}
callOnMock(and(isNotNull(), eq(2)))
这让您的方法的用户承担一些责任。他们必须确保 none 的匹配器被意外调用。
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");
一些上下文
在模拟上设置模拟 (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 以错误的顺序注册了这些匹配器:
eq(2)
eq(1)
argThat(i -> i >= 3)
应该是
eq(1)
eq(2)
argThat(i -> i >= 3)
有没有办法让我操纵这些匹配器的注册顺序?
我现在 org.mockito.AdditionalMatchers
有操作内部堆栈的方法允许匹配器组合(and
、or
、not
)所以至少在内部Mockito 核心是可能的。
是否也可以显式弹出和推送匹配器?
试试这个:
public String callOnMock(int argument2) {
return mock.call(eq(1), eq(argument2), argThat(i -> i >= 3));
}
并这样称呼它:
when(callOnMock(2)).thenReturn("result");
使用 Supplier
:
public String callOnMock(Supplier<Integer> argument2) {
return mock.call(eq(1), argument2.get(), argThat(i -> i >= 3));
}
when(callOnMock(() -> eq(2)).thenReturn("result");
我认为有几种方法可以实现所需的行为。
1。操纵堆栈上匹配器的顺序
This is not the way to go!
matcherStack
似乎是 Mockito 内部的。
他们确实有一个从堆栈 pullLocalizedMatchers
的方法和一个 reportMatcher
将 ArgumentMatcher
压入堆栈的方法。这些可以通过
org.mockito.internal.progress.ThreadSafeMockingProgress
.mockingProgress()
.getArgumentMatcherStorage()
所以理论上你可以选择这条路,但解决方案会很脆弱,因为你会弄乱 Mockito 的内部结构。它们可能会在 Mockito 的后续版本中更改,恕不另行通知。
幸运的是有几个选择。
2。控制匹配器首先注册的顺序
使用Java8Supplier
功能接口(对应@ToYonos给出的
当您调用创建匹配器的方法时,Mockito 会自动注册匹配器(eq
、argThat
、any
、isNotNull
、...)。但是您可以通过为每个匹配器传递 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 风格有点不同。
Special care needs to be taken if you offer convenience methods for those suppliers that use/aggregate other matchers, because of the same problem.
callOnMock(() -> AdditionalMatchers.and(isNotNull(), eq(2)))
will work,
but this will not:public Supplier<Integer> and(int matcher1, int matcher2){ return () -> AdditionalMatchers.and(matcher1, matcher2); } callOnMock(and(isNotNull(), eq(2)))
这让您的方法的用户承担一些责任。他们必须确保 none 的匹配器被意外调用。
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");