Wednesday, January 8, 2014

Invalid use of doThrow() with spy object

Things happened in this morning really embracing me. I’ve made a huge mistake in my programming life because I was confuse on the usage between Mockito.spy() and Mockito.mock().
public class FileClass {
 public void funcB() {
  
  try {
   throw new ClassNotFoundException();
  }
  catch( ClassNotFoundException e ) {
   ...
  }
 }
}
Suppose I have the above class ready for unit test, take note that I have try...catch surrounding the throw statement. My objective is to test whether ClassNotFoundException will be thrown when funcB() is invoke. If I execute the unit test code below, the test will fail with java.lang.AssertionError: Expected exception: java.lang.ClassNotFoundException.
public class TestMainApp {

 @Test(expected=ClassNotFoundException.class)
 public void testFuncB() throws ClassNotFoundException {
  FileClass fc = Mockito.spy(new FileClass());
  fc.funcB();
 }
}
The result of this test was actually fail. The reason being why this test was failed is because the throw statement in funcB() has already been catch. Since the exception has already catch, there were no more exception being escalate from funcB() when the control return to testFuncB(). In order to make this work, I think of a workaround solution. I modify the test code to mimic the ClassNotFoundException is being thrown:
 @Test(expected=ClassNotFoundException.class)
 public void testFuncB() throws ClassNotFoundException {
  FileClass fc = Mockito.spy(new FileClass());
  
  Mockito.doThrow(new ClassNotFoundException())
  .when(fc).funcB();
  
  fc.funcB();
 }
Smart right? NO! This code has actually cause the unit test run even worst. Now I can see MockitoException shown in the stack trace:
java.lang.Exception: Unexpected exception, expected but was
 ... 
 ... 
 ... 
Caused by: org.mockito.exceptions.base.MockitoException: 
Checked exception is invalid for this method!
Invalid: java.lang.ClassNotFoundException
 ... 
 ... 
This is a big mistake. Whenever the class is under test, never mock on the class, just invoke the real instance. Put in the practical way, the use of doThrow() is invalid here because I’m not doing any mock on FileClass. Never think of remove the surrounding try...catch block because this has violated my original design even though it’s working fine.

No comments: