1

I am writing integration tests for my code, and the tests are data driven, different data will give back different result or throw exceptions.

@Test(dataProvider = "successTestData")
public void (String testData1, String testData2) {
    //do something
}

@DataProvider
Object[][] invalidTestData() {
    return new Object[][] {
        {testData2, testData1}
    };
}

Is it possible to add ExpectedException as part of the test data. I understand, I can add like this:

@DataProvider
Object[][] invalidTestData() {
    return new Object[][] {
        {testData2, testData1, expectedException}
    };
}

But how do I use this in the test? I am suppose to provide the expected Exception in the annotation. Is there any way I can use it from the test data? This will help me to write a single test with different test data.

Gautham M
  • 4,816
  • 3
  • 15
  • 37
Mahima
  • 178
  • 8

1 Answers1

1

@Test annotation comes with an attribute expectedExceptions, were you could specify a list of the expected exception that your test method could throw.

Example:

@Test(expectedExceptions = { FileNotFoundException.class, NullPointerException.class }, dataProvider = "invalidTestData" )

Note that, if the exception thrown is not is the mentioned list, then the test would be marked as failed.

So using this, there is no need to pass the exception as part of the dataProvider.

EDIT: If each of the test data throws a different exception, then you could pass it using dataProvider as below:

@DataProvider
Object[][] invalidTestData() {
    return new Object[][] {
        {testData1, NullPointerException.class},
        {testData2, FileNotFoundException.class}
    };
}

Now update the test as:

@Test(dataProvider = "invalidTestData")
public void (String testData, Class<? extends Exception> exceptionType) {
    try {
        // do something with testData
    } catch (Exception e) {
        Assert.assertEquals(e.getClass, exceptionType);
    }
}

EDIT: To handle test cases that expect an exception, but exception is not thrown during actual test run.

@Test(dataProvider = "invalidTestData")
public void (String testData, Class<? extends Exception> exceptionType) {
    try {
        // do something with testData which could possibly throw an exception.

        // code reaches here only if no exception was thrown
        // So test should fail if exception was expected.
        Assert.assertTrue(exceptionType == null);

    } catch (Exception e) {
        Assert.assertEquals(e.getClass, exceptionType);
    }
}

Using assertThrows:

@Test(dataProvider = "invalidTestData")
public void (String testData, Class<? extends Exception> exceptionType) {
    if (exceptionType == null) {
        someMethod(testData);
    } else {        
        Assert.assertThrows(exceptionType, someMethod(testData));
    }
}
Gautham M
  • 4,816
  • 3
  • 15
  • 37
  • if a test case throws an exception where in both the actual and expected are present in the list, then it will be a false positive. By passing in the data provider , I would be able to map it with the test data. – Mahima May 05 '21 at 18:31
  • @Mahima, if our test throws an exception and if it is in the list of expected exceptions then the test would be "sucess". Are you saying that for `testData1` you expect `ExceptionType1` and for `testData2` you expect a different exception `ExceptionType2` ? – Gautham M May 06 '21 at 02:14
  • @Mahima, I have updated my answer. Please check. – Gautham M May 06 '21 at 03:15
  • This works !! However, what I was trying to figure out was a way to set the excepted Exception in the annotation itself. A way where we can modify the annotation during run-time ? I am trying to see if we can use AnnotationTransformer for that, however I couldn't find a working solution. This way, if a case does not expect an exception i.e. a successful case can also be combined into one. – Mahima May 06 '21 at 05:02
  • 1
    @Mahima if the test case does not expect an exception, then you could pass it as `{testData3, null}` right? This way, if the testData does not throw an exception, then catch will not be executed and the test would be a success. If the testData throws an exception, then assertEquals would fail and the test would be a failure as expected. – Gautham M May 06 '21 at 05:13
  • @Mahima, also I don't think that it would be possible using AnnotationTransformer. Because even if we can modify the annotation at runtime, it should be finalized before the method is invoked. So if we are passing the exception type via data provider, then the expected exception would be known only when the method is invoked. But by that time, the annotation is already finalized. I think my solution works even for a successful case if you pass `{testData, null}` – Gautham M May 06 '21 at 06:54
  • 1
    I actually realised later, that if we don't get an exception as expected, then the code flow will not go into the catch block and the case will pass. – Mahima May 13 '21 at 10:19
  • @Mahima that could actually be handled within the try block. Code would reach the end of the try block only if no exception is thrown. I have updated the answer. – Gautham M May 13 '21 at 10:33
  • 1
    Just wanted to put it out here. We can also use `Assert.assertThrows(expectedException, () -> );` – Mahima May 13 '21 at 14:20
  • @Mahima this should be used only when `expectedExceptionType` is not null. – Gautham M May 13 '21 at 14:50