我有两个类:
public class BaseClass <T> {
private final T config;
...
@NotNull
public final T getConfig() {
return config;
}
}
public class DerivedClass extends BaseClass<MyConfig> {
...
}
还有一个测试:
...
MyConfig myConfig = mock(MyConfig.class);
DerivedClass child = mock(DerivedClass.class);
when(child.getConfig()).thenReturn(myConfig); // this line generates the error
我得到一个错误,getConfig()
返回null,而不是myConfig
。我对同一个mock对象进行了其他方法调用,使用when/return模式可以像预期的那样工作,所以我尝试了多态性。当我删除了对该方法的最后限制并在派生类中重写它(只是调用超级版本)时,mock工作正常。
我不想为了测试而重新设计产品代码,降低API的刚性,所以上面的继承更改不是一个可以接受的解决方案。如何模拟对超类方法的调用?
来自Mockito的常见问题解答:
Mockito的局限性是什么
[…]
- 不能模拟final方法-它们的实际行为在执行时没有任何异常。Mockito不能警告你嘲笑最终方法,所以要提高警惕
[…]
为此,您需要像PowerMock这样的扩展。有关工作示例,请参阅此问题。PowerMock的文档中提供了完整的描述
遗憾的是,您无法模拟final方法。但需要提醒的是:使用PowerMock并不能解决这个问题。相反:转向PowerMock意味着进入一个没有经过大量精心准备就不应该进入的领域。
PowerMock操纵字节码,允许您覆盖final或静态方法。这意味着它会让你面临各种各样奇怪的问题;很多时候没有正当理由;但很可能会浪费大量时间寻找与测试中的代码无关的"bug"。PowerMock的另一个大问题是,它使大多数"覆盖"框架变得无用(因为这些框架还操纵字节码…)
因此,在您的情况下:实际上,使该方法成为最终方法是一个很好的设计。因为这强调了开放/封闭原则。因此,从概念角度来看,有两种选择可以"修复"这个问题:
a) 您允许对"config"对象进行依赖注入(例如,通过提供一个接受"config"的受保护构造函数)
b) 你记得FCoI,并避免做基类/子类的事情完全