org.hyphenType.unittesting
Class NonExceptionalExit

java.lang.Object
  extended by java.lang.Throwable
      extended by java.lang.Exception
          extended by java.lang.RuntimeException
              extended by org.hyphenType.unittesting.NonExceptionalExit
All Implemented Interfaces:
Serializable

public final class NonExceptionalExit
extends RuntimeException

A (strange) exception that is thrown to simulate JVM exit when we utilize MockArgumentsInvocationHandler. Behind the scenes, the MockArgumentsInvocationHandler will utilize a UnitTestingOptionExtractor, which in turn is responsible for throwing exceptions of type NonExceptionalExit. There are cases in which programmers may chose to design their algorithms under the assumption that a call to methods Options#exit(Enum) or Options#exit(int) will prevent subsequent code to be executed. This is the case, for instance, in the following code:

 
 public static int x(Opt opt) {
        if (!opt.a())
                opt.exit(17);
        return 12;
 }
 
 
Assume that opt.a() returns true. In such case, execution of the method x(Opt opt) should never reach the return statement, even when we execute this method during unit testing. Not suspending the execution of the method x(Opt opt) when we reach the statement opt.exit(1) may cause the behavior of the code under test to change during unit testing. To prevent such behavior change, this exception ( NonExceptionalExit) is silently thrown every time to methods Options#exit(Enum) or Options#exit(int) are called.

Since NonExceptionalExit is a RuntimeException, procedures that call Options#exit(Enum) or Options#exit(int) directly do not need to catch the exception for the source code to compile. Note that there is no exception handling in the method x(Opt opt) above. On the other hand, unit tests that cause the execution of methods such as x(Opt opt) above will need to catch this exception. This is demonstrated in the following code:
 
 @Test
 public void testX() {
        UnitTestingOptionExtractor unitTestingOptionExtractor = new UnitTestingOptionExtractor(Opt.class);
        Opt opt = oe.options("-b");
        try {
                x(opt); // Calling x(Opt opt) as defined above
                assertFail("We should not reach this line.");
        }
        catch(NonExceptionalExit e) {
                assertTrue(unitTestingOptionExtractor.exitIntCalled(opt));
        }
 }
 
 
In short, throwing this exception does not mean a failure (that's why this exception is called NonExceptionalExit), but is only a means to interrupt execution of a procedure under test. To prevent UnitTestingOptionExtractor to throw this exception (strictly speaking, to prevent MockArgumentsInvocationHandler to throw this exception), use the constructor UnitTestingOptionExtractor.UnitTestingOptionExtractor(Class, boolean) and pass false as the second argument.

This class also provides the following methods to check how Options#exit(int) or Options#exit(Enum) was called, and which of them was called: Those methods are equivalent to: Here is an example on how to use those methods in unit testing:
 
 @Test
 public void testX() {
        UnitTestingOptionExtractor unitTestingOptionExtractor = new UnitTestingOptionExtractor(Opt.class);
        Opt opt = oe.options("-b");
        try {
                x(opt); // Calling x(Opt opt) as defined above
                assertFail("We should not reach this line.");
        }
        catch(NonExceptionalExit e) {
                assertTrue(e.exitIntCalled());
                assertEquals(17, getStatusCodeInt());
        }
 }
 
 
Additionally, the method exitCallPoint() can be used to find in which class, file, and line is the statement that called Options#exit(Enum) or Options#exit(int):
 
 @Test
 public void sample1() throws InvalidOptionsInterfaceException, InvalidOptionException {
        UnitTestingOptionExtractor optionExtractor = new UnitTestingOptionExtractor(AnotherOptions.class);
        try {
                AnotherApplication.main(optionExtractor.options("-x"));
                fail("Should have thrown an exception on the line above.");
        }
        catch (NonExceptionalExit e) {
                assertEquals(AnotherApplication.class.getName(), e.exitCallPoint().getClassName());
                // Line number detection won't work unless classes were compiled using debug mode.
                assertEquals(6, e.exitCallPoint().getLineNumber());
                assertEquals("main", e.exitCallPoint().getMethodName());
        }
 }
 
 
The return type of exitCallPoint() is StackTraceElement, which is obtained via Throwable.getStackTrace(). Therefore, the stack trace element returned by exitCallPoint() will only have the line number data if the classes were compiled using debug mode. If classes were not compiled with debug mode, the standard behavior is that those line numbers will be all set to -1.

Author:
Aurelio Akira M. Matsui
See Also:
UnitTestingOptionExtractor, MockArgumentsInvocationHandler, Options#exit(Enum), Options#exit(int), Serialized Form

Method Summary
 StackTraceElement exitCallPoint()
           
 boolean exitEnumCalled()
           
 boolean exitIntCalled()
           
 Enum<?> getStatusCodeEnum()
           
 int getStatusCodeInt()
           
 
Methods inherited from class java.lang.Throwable
fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toString
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Method Detail

getStatusCodeEnum

public Enum<?> getStatusCodeEnum()

exitEnumCalled

public boolean exitEnumCalled()

getStatusCodeInt

public int getStatusCodeInt()

exitIntCalled

public boolean exitIntCalled()

exitCallPoint

public StackTraceElement exitCallPoint()


Copyright © 2013. All Rights Reserved.