嗨,我有这段代码设计不是很好,但我不是这段代码的拥有者所以我无法改变它。
public interface Car{ // This is a marker interface. }
public class BigCar implements Car{
public boolean isVeryBig(){
return true;}
}
public class QuiteBigCar implements Car{
public boolean isVeryBig(boolean withHatchBack){
return true;}
}
public Pickup implements Car{
public boolean isVeryBig(boolean withHatchBack, boolean withRoofRack){
return true;}
}
您会看到界面仅存在,告诉我BigCar
,QuiteBigCar
和Pickup
"是"汽车。不是很聪明,但那是我必须要处理的事情。
现在我有一个接收Car作为参数的方法,并将返回一个模拟版本的汽车。我希望模拟确保每次调用isVeryBig()
时,无论方法签名是什么,它都将返回false 。问题是我没有所有isVeryBig()
方法的通用界面;他们都有不同的签名。
public Car mockMyCar(Car car){
Car mockedCar = mock(car);
when(mockedCar.isVeryBig()) <-- This wont work since the method doesn't exist on the interface
return mockedCar;
}
是的,我可以将Car投射到他们的子类中但这对我来说不是一个选项,因为有太多的类实现了Car,做一个instanceOf检查所有实现会使代码非常讨厌而且我不会控制未来汽车的新实施。
有什么想法吗?
答案 0 :(得分:4)
一些选项,从最少hacky到最hacky:
承认在这里嘲笑一辆汽车就像模仿一个Serializable一样没有意义,你应该选择一个实现来模拟。你不会在模拟具体的BigCar或Pickup时遇到同样的麻烦,你可以在几个测试用例中运行一些不同的实现。
重构为TrustNoOne描述的常见正确多态接口,而不是像你在这里那样使用ersatz多态。我知道你可能会被绑住,但是接下来遇到这个问题的人可能不会。
在创建模拟时,您可以执行类似于您所描述的操作 - 按名称匹配方法而不是签名 - supplying a default Answer:
Car car = Mockito.mock(Car.class, new Answer<Object>() {
@Override public Object answer(InvocationOnMock invocation) {
if (invocation.getMethod().getName().equals("isVeryBig")) {
return false;
}
// Delegate to the default answer.
return Mockito.RETURNS_DEFAULTS.answer(invocation);
}
};
请记住,您的特定情况(能够向下转换为具有不同签名的各种实现之一)可能需要使用extraInterfaces功能来根据需要使Java向下转换,这使得这一切都很棘手和脆弱。您可能最好使用上述其他解决方案之一。