问题
I am using Mockito services to test the Exception that could be thrown in MyFinalClass and be caught in MyAbstractClass as the concrete method makes a call to the getObject method in MyFinalClass.
Please see below code.
public abstract class MyAbstractClass{
@Autowired
private MyFinalClass myFinalClass;
//concrete method
protected Object myConcreteMethod(String name){
try{
Object b = myFinalClass.getObject(name);
return b;
} catch(MyException e){
LOGGER.log("ERROR THROWN" + e);
}
}
}
public final class MyFinalClass {
public Object getObject(String name) throws MyException{
**** HAS NOT YET BEEN IMPLEMENTED ****
return null;
}
}
public MyException extends Exception{
....
}
Below is the test class I have written. Unfortunately I am having issues with Mockito.when(myFinalClass).getObject(name).thenReturn(Mockito.mock(Object.b)), as it does not seem to get called.
@ContextConfiguration(locations = "classpath:applicationContext-test.xml")
@RunWith(PowerMockRunner.class)
public class MyAbstractClassTest {
public static class ExampleConcreteClass extends MyAbstractClass{
}
@InjectMocks
ExampleConcreteClass exampleConcreteClass;
@Mock
MyFinalClass myFinalClass;
@Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
@Test
public void testMyConcreteMethod(){
try{
Mockito.when(myFinalClass).getObject(name).
thenThrow(new MyException());
exampleConcreteClass.myConcreteMethod();
Assert.fail();
}catch(MyException e){
Assert.assertTrue(Boolean.TRUE);
}
}
}
Because the implementation has not yet been completed for the getObject(String name) method in MyFinalClass, I am throwing new MyException() on the call of the getObject method. However the exception does not get thrown as the Mockito.when(...) call does not get called, causing Assertion to fail.
Please help. Thank you.
回答1:
Mockito is not able to mock out final classes. There are a few ways to get around this.
1) In MyAbstractClass
consider not referencing a final class, and instead reference an interface or abstract class that MyFinalClass
implements (or extends). So lets say that MyFinalClass
implements an interface called MyGettableApi
. Then MyAbstractClass
would look like this.
public abstract class MyAbstractClass{
@Autowired
private MyGettableApi gettableService;
//concrete method
protected Object myConcreteMethod(String name){
try{
Object b = gettableService.getObject(name);
return b;
} catch(MyException e){
LOGGER.log("ERROR THROWN" + e);
}
}
}
public final class MyFinalClass implements MyGettableApi {
@Override
public Object getObject(String name) throws MyException{
**** HAS NOT YET BEEN IMPLEMENTED ****
return null;
}
}
public interface MyGettableApi {
public Object getObject(String name);
}
Now in your test code you can mock out MyGettableApi
, since it is not final, without a problem.
@Mock
MyGettableApi myFinalClass;
2) Another way around this is wrap the call to myFinalClass.getObject(name);
wrapped in another method.
public abstract class MyAbstractClass{
@Autowired
private MyFinalClass myFinalClass;
//concrete method
protected Object myConcreteMethod(String name){
try{
Object b = myGetObjectWrapperMethod(name);
return b;
} catch(MyException e){
LOGGER.log("ERROR THROWN" + e);
}
}
Object myGetObjectWrapperMethod(String name) {
return myFinalClass.getObject(name);
}
}
Now in your test code you would @Spy
the instance of MyAbstractClass
that is being tested.
@InjectMocks
@Spy
ExampleConcreteClass exampleConcreteClass;
then you can mock out our new wrapper method.
doThrow(new MyException()).when(exampleConcreteClass).myGetObjectWrapperMethod(name);
来源:https://stackoverflow.com/questions/45891213/mocking-chained-calls-in-concrete-class-mockito