Thursday, January 17, 2013

Threw an exception from a method that throws no exceptions -- wait, what?


Here's a fun little false positive I ran into recently.

Application (ClassBeingTested) believes Library (ClassBeingMocked) throws an exception, but Library really does not.  Yet Application's unit-test case passes! This takes an interplay of JUnit, Jmock, and their use of reflection.

Library:
public interface ClassBeingMocked {
    public void neverThrowsAnException();
}

public class ClassBeingMockedImpl implements ClassBeingMocked {
    public void neverThrowsAnException() {
        System.out.println("ClassBeingMocked: in the method which does not throw an exception");
    }
}

Application:

public class ClassBeingTested {

    private final ClassBeingMocked _them;

    public ClassBeingTested(ClassBeingMocked them) {
        _them = them;
    }

    public void neverThrowsAnExceptionEither() {
        try {
            _them.neverThrowsAnException();
        }
        catch (Exception e) {
            System.out.println("ClassBeingTested#neverThrowsAnExceptionEither: " +
                "caught \"" + e.toString() + "\"");
        }
    }

}

Test code:

public class MyTest {
    private Mockery _mockery;
    private ClassBeingMocked _mockedInstance;
    private ClassBeingTested _testedInstance;

    @Before

    public void setup() {
        _mockery = new Mockery();
        _mockedInstance = _mockery.mock(ClassBeingMocked.class);
        _testedInstance = new ClassBeingTested(_mockedInstance);
    }

    @Test

    public void testMethod() {

        _mockery.checking(new Expectations() {{
            one(_mockedInstance).neverThrowsAnException();
            will(throwException(new Exception("we expect an exception on timeout")));
            }});
        _testedInstance.neverThrowsAnExceptionEither();
       _mockery.assertIsSatisfied();
    }    

}

What prints is:
JUnit version 4.10
..ClassBeingTested#neverThrowsAnExceptionEither: caught "java.lang.IllegalStateException: tried to throw a java.lang.Exception from a method that throws no exceptions"

Time: 0.021

OK (4 tests)

One handy item is the print to stdout in the application's exception handler; without that, the misunderstanding proceeds silently.  Yet this will disappear in logging contexts.

One solution is a narrower catch:  If the application instead does

try { _them.neverThrowsAnException(); } catch (MoreSpecificExceptionType e) { ... }
then this is caught at compile time:
"exception MoreSpecificExceptionType is never thrown in body of corresponding try statement"
This must be used carefully in a catch-all-exceptions server-thread context.

No comments:

Post a Comment