Testing various exceptions in loaders

Hi everyone

Just a quick note for fellow developers.
As we all know, it is often a pain to meet the very high test coverage ratio in our SonarQube instance.
One part that I found particularly annoying is to check for NumberFormatExcption or IOException in various loaders.
For the first ones, I often used specific test resource files created by copying a regular file and replacing one entry with random strings (like “####” or “not-a-number”). For the second ones, I basically always gave up as generating an IOException in a way that is reproducible in all operating systems was hard.

I finally found something simple in the case of parsers based on DataSource, using some specific inline readers set up in the tests themselves, without a need to create a specific test resource file.

Here are two examples I used in the CCIRLoaderTest. The first uses a DataSource built from a StringReader with a non-numerical field to trigger a NumberFormatExcption:

public void testParsingError() {
    try {
        new CCIRLoader().
            loadData(new DataSource("dummy", () -> new StringReader("\n" +
                                                                    "  0.52396593E+01 -0.56523629E-01 -0.18704616E-01  0.12128916E-01\n" +
                                                                    "  0.79412200E-02 -0.10031432E-01  0.21567253E-01 -0.68602669E-02\n" +
                                                                    "  0.37022347E-02  0.78359321E-02  0.63161589E-02 -0.10695398E-01\n" +
                                                                    "  0.29390156E-01  not-a-number   -0.28997501E-01  0.10946779E+00\n")));
        Assertions.fail("an exception should have been thrown");
    } catch (OrekitException oe) {
        Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
        Assertions.assertEquals(5, (Integer) oe.getParts()[0]);
        Assertions.assertEquals("dummy", oe.getParts()[1]);
        Assertions.assertEquals("0.29390156E-01  not-a-number   -0.28997501E-01  0.10946779E+00", oe.getParts()[2]);
    }
}

The second one is more interesting, it generates an IOException by using a DataSource built from a PipedReader. The trick is that the PipedReader is not connected, so the exception is triggered. One interesting property is that the exception is triggered only on the first attempt to read data, it is not triggered when the reader is opened, so it allows to test for IOException handling within read loops, which is something I found difficult to achieve.

public void testIOException() {
    try {
        new CCIRLoader().loadData(new DataSource("exception", () -> new PipedReader()));
        Assertions.fail("an exception should have been thrown");
    } catch (OrekitException oe) {
        Assertions.assertEquals(IOException.class, oe.getCause().getClass());
        Assertions.assertEquals(OrekitMessages.NEQUICK_F2_FM3_NOT_LOADED, oe.getSpecifier());
        Assertions.assertEquals("exception", oe.getParts()[0]);
    }
}

Hope this trick will be helpful to other developers.

2 Likes