Mockito,argThat和hasEntry

Wes*_*ley 3 java testing hamcrest mockito

tl; dr:由于类型参数不匹配,这些测试无法编译。我应该进行哪些更改以使其能够正确编译和运行?

https://github.com/wesleym/matchertest

我有一些调用服务的非测试代码。它使用map参数调用服务的activate方法。

public class Foo {
  private final Service service;

  public Foo(Service service) {
    this.service = service;
  }

  public void bar() {
    Map<String, ?> params = getParams();
    service.activate(params);
  }

  private Map<String, ?> getParams() {
    // something interesting goes here
  }
}
Run Code Online (Sandbox Code Playgroud)

我要测试的某些代码对这样的服务有依赖性:

public interface Service {
  public void activate(Map<String, ?> params);
}
Run Code Online (Sandbox Code Playgroud)

我想通过使用Mockito模拟服务并验证是否使用合理的映射调用了代码来测试此代码。以下代码有效:

@Test
public void testExactMap() {
  Service mockService = mock(Service.class);
  Foo foo = new Foo(mockService);

  foo.bar();

  Map<String, String> expectedParams = new HashMap<>();
  expectedParams.put("paramName", "paramValue");
  verify(service).activate(expectedParams);
}
Run Code Online (Sandbox Code Playgroud)

但是,我只想测试地图是否包含一个特定条目。Hamcrest hasEntry匹配器似乎很适合此用例:

@Test
public void testHasEntry() {
    Service mockService = mock(Service.class);
    Foo foo = new Foo(mockService);

    foo.bar();

    verify(mockService).activate(argThat(hasEntry("paramName", "paramValue")));
}
Run Code Online (Sandbox Code Playgroud)

尝试此操作时,我在IntelliJ IDEA中收到以下错误:

Error:(31, 45) java: incompatible types: inference variable T has incompatible bounds
    equality constraints: java.util.Map<? extends java.lang.String,? extends java.lang.String>
    upper bounds: java.util.Map<java.lang.String,?>,java.lang.Object
Run Code Online (Sandbox Code Playgroud)

这里的问题是我需要一个Mockito匹配器Map<String, ?>,但是hasEntry给我一个的匹配器Map<? extends String, ? extends String>。即使使用显式类型参数,我也无法弄清楚该如何协调类型参数的“?extends”部分。我应该怎么做才能解决这个错误?我应该使用特定的类型转换还是显式类型参数?

我了解可以为此使用ArgumentCaptor。这真的是唯一的方法吗?Hamcrest匹配器完全有可能吗?

Vin*_*Pro 5

由于argThat某种原因未推断出返回类型。尝试如下所示显式转换:

 Mockito.verify(foo).bar((Map<String, String>) argThat(Matchers.hasEntry("paramName", "paramValue")));
Run Code Online (Sandbox Code Playgroud)

所述testHasEntryCast()可固定,如下所示。注意,强制转换(Map<String, ?>)argThat返回类型:

@Test
public void testHasEntryCast() {
    Service mockService = mock(Service.class);
    Foo foo = new Foo(mockService);

    foo.bar();

    verify(mockService).activate((Map<String, ?>)  argThat(hasEntry("paramName", "paramValue")));
}
Run Code Online (Sandbox Code Playgroud)