【问题标题】:mocking a method that return generics with wildcard using mockito使用 mockito 模拟使用通配符返回泛型的方法
【发布时间】:2013-04-11 07:21:08
【问题描述】:

我使用的是 mockito 1.9.5。 我有以下代码:

public class ClassA  {

public List<? extends MyInterface> getMyInterfaces() {
    return null;
}

public static void testMock() {
    List<MyInterface> interfaces = new ArrayList<>();
    ClassA classAMock = mock(ClassA.class);
    when(classAMock.getMyInterfaces()).thenReturn(interfaces);      
}

thenReturn(interfaces) 出现编译错误:

"The method thenReturn(List<capture#1-of ? extends MyInterface>) in the type 
 OngoingStubbing<List<capture#1-of ? extends MyInterface>> is not applicable for the arguments 
 (List<MyInterface>)"

但是,当我使用 mockito 的 thenAnswer 方法时,我没有收到错误消息。谁能告诉我发生了什么事?为什么我在使用thenReturn 方法时会收到错误消息? 当ClassA由第三方提供且无法修改时,有没有其他方法可以解决这个问题?

【问题讨论】:

  • getMyInterfaces 的返回类型中不应包含通配符。这不是一个好的做法,因为该 api 的客户端必须处理他们不知道的通配符。
  • 我不控制这个 API。它是由第 3 方提供的。
  • @SpaceTrucker 我认为拥有像List&lt;? extends MyInterface&gt; 这样的返回类型是非常明智的做法。这意味着您(作为方法调用者)可以将MyInterface 对象从列表中取出,但您不能将任何内容放入其中。

标签: java generics mockito


【解决方案1】:

编辑:从 Mockito 1.10.x 开始,嵌入在类中的泛型类型现在被 Mockito 用于深度存根。 IE。

public interface A<T extends Observer & Comparable<? super T>>  {
  List<? extends B> bList();
  T observer();
}

B b = deep_stubbed.bList().iterator().next(); // returns a mock of B ; mockito remebers that A returns a List of B
Observer o = deep_stubbed.observer(); // mockito can find that T super type is Observer
Comparable<? super T> c = deep_stubbed.observer(); // or that T implements Comparable

Mockito 尽最大努力获取编译器嵌入的类型信息,但是当应用擦除时,mockito 只能返回 Object 的模拟。


原创:嗯,这更多是泛型的问题,而不是 Mockito 的问题。对于泛型,您应该阅读 Angelika Langer 在它们上写的内容。对于当前主题,即通配符,请阅读此section

但简而言之,您可以使用 Mockito 的其他语法来帮助您解决当前的情况:

doReturn(interfaces).when(classAMock).getMyInterfaces();

或者使用 BDD 别名:

willReturn(interfaces).given(classAMock).getMyInterfaces();

尽管如此,您可以编写更通用友好的包装器。这将有助于未来的开发人员使用相同的第 3 方 API。


附带说明:您不应该模拟您不拥有的类型,这可能会导致许多错误和问题。相反,你应该有一些包装。例如 DAO 和存储库就代表了这样的想法,一个人将模拟 DAO 或存储库接口,而不是 JDBC / JPA / hibernate 的东西。有很多关于此的博客文章:

【讨论】:

  • 也感谢您提供有见地的链接!
  • doReturn 语法不是类型安全的,但是(你可以返回任何东西,而不仅仅是一些东西的列表)。 (尽管在此设置期间您仍然会遇到异常 - 但如果您放入不同类型的列表,则不会。)
  • @PaŭloEbermann 事实上,这是由于类型擦除。在这种情况下,检查是在运行时完成的。另一种形式可以在编译时进行类型检查。
【解决方案2】:

另一种解决方案(尽管可读性较差)是限定when 的静态方法调用以绑定通配符:

Mockito.<List<? extends MyInterface>>when(classAMock.getMyInterfaces()).thenReturn(interfaces);

【讨论】:

  • 可能是因为类型擦除阻止了 Mockito 从泛型获取更多信息,所以它不知道泛型是 > 还是 extends MyInterface>,通过在方法前面添加泛型的类型,可以防止类型擦除引起的猜测。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-10
  • 1970-01-01
  • 2021-02-12
  • 2020-01-21
  • 2016-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多