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

问题描述 投票:0回答:3

Some Context

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

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

我说的是第二种风格。

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

What I try to achieve

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

例如:

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

将使用这样:

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

The problem

这不起作用,因为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核心内部是可能的。

是否也可以明确地弹出和推送匹配器?

mockito matcher
3个回答
1
投票

使用Supplier

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

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

0
投票

试试这个:

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

并称之为:

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

0
投票

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

1. Manipulate the order of matchers on the stack

这不是要走的路!

matcherStack似乎是Mockito的内部。 他们确实有一个方法来从堆栈pullLocalizedMatchersreportMatcher方法将ArgumentMatcher推入堆栈。这些可以通过访问

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

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

幸运的是有几种选择。

2. Control the order in which matchers are registered in the first place

使用Java 8 Supplier功能接口(这对应于@ToYonos给出的this answer

当您调用创建它们的方法时,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. Control the order in which mocks expect matchers

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

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");
© www.soinside.com 2019 - 2024. All rights reserved.