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.