Hi I have this piece of code that is not very well designed, but I'm not the owner of this code so I can't change it.
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;}
}
You see the interface only exists to let me know that BigCar
, QuiteBigCar
and Pickup
"is a" Car. Not very clever, but that's what I have to deal with.
Now I have a method that receives a Car as a param and will returned a mocked version of the car. I want the mock to make sure every time isVeryBig()
is called, it will return false regardless of the method signature. The problem is I don't have a common interface for all isVeryBig()
methods; they all have a different signature.
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;
}
Yes, I could cast Car into their subclass but this is not an option for me as there is too many classes that implement Car, make a instanceOf check on all implementation would make the code very nasty and I don't control new implementations of Car in the future.
Any idea?
A few options, in order from least-hacky to most-hacky:
Acknowledge that mocking a Car here makes as little sense as mocking a Serializable, and that you should actually pick an implementation to mock. You won't have the same trouble mocking a concrete BigCar or Pickup, and you can run through a few different implementations in a few test cases.
Refactor to a common properly-polymorphic interface as TrustNoOne described, rather than using ersatz polymorphism the way you have here. I know you may have your hands tied, but whoever stumbles across this question next might not.
You can do something similar to what you're describing—matching methods by name instead of signature—by supplying a default Answer when you create the mock:
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);
}
};
Bear in mind, though, that your particular situation (of being able to downcast to one of a variety of implementations with different signatures) may require use of the extraInterfaces feature to let Java downcast as needed, which makes this entire thing tricky and fragile. You'd probably be better off using one of the other above solutions instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With