21

In JUnit4 you can write parameterized unit tests by providing parameters collection in one method, which will be passed to the constructor of the test and testing in another method. If I have a parameter for which I expect an exception to be thrown, how do I specify that?

Yarix
  • 1,061
  • 10
  • 18
Gabriel Ščerbák
  • 16,864
  • 8
  • 33
  • 50

7 Answers7

30

this is how i use junit parameterized test with expected exceptions:

@RunWith(Parameterized.class)
public class CalcDivTest {

@Parameter(0)
public int num1;
@Parameter(1)
public int num2;

@Parameter(2)
public int expectedResult;

@Parameter(3)
public Class<? extends Exception> expectedException;
@Parameter(4)
public String expectedExceptionMsg;

@Rule
public ExpectedException thrown = ExpectedException.none();

@Parameters
public static Iterable<Object[]> data() {
    return Arrays.asList(new Object[][] {
        // calculation scenarios:
        { 120, 10, 12, null, null }, // simple div  
        { 120, 0, -1, ArithmeticException.class, "/ by zero" }, // div by zero          
    });

}

@Test
public void testDiv() throws CCalculationException {

    //setup expected exception
    if (expectedException != null) {
        thrown.expect(expectedException);
        thrown.expectMessage(expectedExceptionMsg);
    }

    assertEquals("calculation result is not as", expectedResult, div(num1, num2) );

}

private int div(int a, int b) {
    return a/b;
}
}
Yarix
  • 1,061
  • 10
  • 18
7

In contrast to what other suggest, I would not introduce any kind of logic to tests - even simple ifs!

What you should have are two testing methods:

  • first one takes valid parameters (and expects some output)
  • second takes invalid parameters (and expects exceptions)

Not sure if JUnit with its constructor-based parametrized testing is able to do this. Probably you would have to create two test classes for this. Go with JUnit Params or TestNG which offer much more convenient solution.

5
if (parameter == EXCEPTION_EXPECTED) {
    try {
        method(parameter);
        fail("didn't throw an exception!");
    } catch (ExpectedException ee) {
        // Test succeded!
    }
}
Benoit Courtine
  • 6,716
  • 27
  • 40
  • 3
    I hoped for a more reasonable solution - e.g. something like multiple @Parameters methods with optional expected annotation argument, but I guess something like that is not a part of the framework. Thank you anyway. – Gabriel Ščerbák Nov 11 '10 at 08:40
  • Having a try-catch in a test case is not the best way to achieve this. I would select @Yarix 's answer. – ehsun7b May 09 '19 at 07:41
5

I agree with Tomek, and would go with two tests. The first tests for cases where no exceptions are expected. The second tests for values that should result in exceptions being thrown (i.e., and fails if they are not thrown).

Below is a simple example, where the implementation of ExceptionThrower.throwAnInstanceException(int) simply throws an IllegalArgumentException when the supplied int is less-than-1. In your implementation, all supplied values should trigger the exception.

@ParameterizedTest
@ValueSource(ints = {0, 1})
public void parameterizedIntExceptionTest(int testValue) {
    ExceptionThrower exceptionThrower = new ExceptionThrower();

    assertThrows(IllegalArgumentException.class, () -> {
        exceptionThrower.throwAnInstanceException(testValue);
    });
}

If you wanted to supply multiple arguments, then you'd be looking at using a MethodSource vice a ValueSource for the test.

SoCal
  • 439
  • 7
  • 17
1

Gabriel, please look at TestWatcher rule (since JUnit 4.9). Here is the sample code quoted from http://junit-team.github.io/junit/javadoc/4.11/org/junit/rules/TestWatcher.html:

public static class WatchmanTest {
    private static String watchedLog;

    @Rule
    public TestWatcher watchman= new TestWatcher() {
        @Override
        protected void failed(Throwable e, Description description) {
            watchedLog+= description + "\n";
        }

        @Override
        protected void succeeded(Description description) {
            watchedLog+= description + " " + "success!\n";
        }
     };


     @Test
     public void fails() {
         fail();
     }

     @Test
     public void succeeds() {
     }
 }

Another approach would be to use ErrorCollector from JUnit 4.7: @Rule public ExpectedException thrown = ExpectedException.none();

@Test
public void testCollectingErrors() {
    thrown.handleAssertionErrors();
    thrown.expect(MultipleFailureException.class); // or #expectMessage()/#expectCause()

    collector.checkThat("a", equalTo("b"));
    //...
}
Noumenon
  • 3,184
  • 4
  • 39
  • 57
n0mer
  • 300
  • 4
  • 9
0

If you used catch-exception instead of the corresponding annotations and rules of JUnit4, then your code would look like this:

catchException(obj).method(parameter);

if (parameter != EXCEPTION_EXPECTED) {
    assert caughtException() instanceof ExpectedException;
}
// more assertions
rwitzel
  • 1,525
  • 14
  • 19
0
@Test(expected = Exception.class)
@Parameters(value = { "invalidInput1", "invalidInput2" })
public void shouldThrowOnInvalidInput(String input) {
    ClassToTest.methodToTest(input);
}

Using junitparams.Parameters from junitparams library.

embuc
  • 364
  • 3
  • 4