Java 模拟继承的最终方法

Java 模拟继承的最终方法,java,junit,mockito,Java,Junit,Mockito,我有两门课: public class BaseClass <T> { private final T config; ... @NotNull public final T getConfig() { return config; } } public class DerivedClass extends BaseClass<MyConfig> { ... } 我得到一个错误,getConfig()返回null,而不是myConfig

我有两门课:

public class BaseClass <T> {
  private final T config;
  ...
  @NotNull
  public final T getConfig() {
    return config;
  }
}

public class DerivedClass extends BaseClass<MyConfig> {
  ...
}
我得到一个错误,
getConfig()
返回null,而不是
myConfig
。我使用when/return模式对同一模拟对象进行了其他方法调用,因此我使用了多态性。当我删除了对该方法的最后一个限制并在派生类中重写它(只调用超级版本)时,mock工作正常

我不想为了测试而重新设计产品代码,降低API的刚性,因此上面的继承更改不是一个可接受的解决方案。如何模拟对超类方法的调用?

来自:

Mockito的局限性是什么?
[……]

  • 无法模拟最终方法-它们的真实行为是毫无例外地执行的。Mockito无法警告您有关模拟最终方法的信息,因此请保持警惕
[……]


为此,您需要像这样的扩展。有关工作示例,请参见。在

中可以找到完整的描述,不幸的是,您不能模拟final方法。但有一句警告:转向PowerMock并不是这个问题的答案。相反:转向PowerMock意味着进入一个没有大量仔细准备就不应该进入的领域

PowerMock操纵您的字节码以允许您重写final或static方法。这意味着它会让你面临各种各样奇怪的问题;很多时候都没有好的理由;但很有可能会浪费大量时间寻找与测试代码无关的“bug”。PowerMock的另一个大问题是,它使大多数“覆盖”框架变得无用(因为这些框架还操纵字节码…)

所以在你的例子中:实际上,让这个方法成为最终结果是一个很好的设计。因为这强调了。因此,从概念上讲,有两个选项可以“修复”这个问题:

a) 您允许该“config”对象的依赖项注入(例如,通过提供一个接受“config”的受保护构造函数)


b) 您记住并避免完全使用基类/子类的东西

“模仿继承的方法”--您总是可以对其父代进行粗鲁的评论,但这在政治上是不正确的(抱歉,无法抗拒标题:-)提示:至少应该提到PowerMock有很多缺点。如果没有“通常情况下,你根本不应该使用它”,我绝不会向其他人推荐它。Bertrand Meyer(“开-关”原则的创建者)写道(对不起,我现在似乎找不到它)强烈反对编程语言(比如C++),在默认情况下,方法是不可重写的(不是虚拟的),或者甚至允许方法是最终的,因为这会阻止OCP的应用。因此,虽然使方法和类成为final是很好的设计,但它完全不强调OCP。相反,它强调了GoF的“组合优先于继承”原则。如果你没有办法阻止子类覆盖某个方法,你如何关闭一个类进行修改?!OCP的典型用例是当您有一个抽象基类,该基类提供一个或多个要调用的最终方法,这些方法本身调用抽象方法。如果这些方法不是最终的,你可以改变子类的行为。事实上,不,在OCP中(如Meyer的书和中所描述的)一个“关闭”的类仅仅是一个已经投入生产的类,因此在破坏客户机代码的惩罚下不应该再修改它。请注意,这是作者所说的;我觉得这个原则是完全错误和过时的。在OCP作者看来,类应该保持“开放扩展”,也就是说,它不应该被声明为
final
,也不应该将其任何非
private
方法声明为
final
...
MyConfig myConfig = mock(MyConfig.class);
DerivedClass child = mock(DerivedClass.class);
when(child.getConfig()).thenReturn(myConfig); // this line generates the error