Let JUnit Handle Exceptions

 class​ LogbookTest {
 
  @Test
 void​ readLogbook() {
  Logbook logbook = ​new​ Logbook();
 
 try​ {
  List<String> entries = logbook.readAllEntries();
  Assertions.assertEquals(13, entries.size());
  } ​catch​ (IOException e) {
» Assertions.fail(e.getMessage());
  }
  }
 
  @Test
 void​ readLogbookFail() {
  Logbook logbook = ​new​ Logbook();
 
 try​ {
  logbook.readAllEntries();
» Assertions.fail(​"read should fail"​);
  } ​catch​ (IOException ignored) {}
  }
 }

Tests and exceptions often go hand-in-hand. Tests ensure that no exceptions are thrown, or that a particular exception must be thrown.

Let’s look at the code. There’s two examples of tests that depend on exceptions in different ways. The first one fails if an IOException occurs and the second one succeeds in that case. Here we make the tests fail by executing the fail() assertion. The first test catches the IOException and fails, but it hands over the message of the exception for a better traceability of the failed test. The second test succeeds when the exception occurs. This will take the control flow into the catch block and avoid the call to fail().

These two ways of testing for exceptions work, but they come with some downsides. In the first test, we break the cause chain (and we know we should Avoid Breaking the Cause Chain, of course). We provide only the message, not the full exception with its stack trace. In the second test, we actually expect the exception to occur, but the code doesn’t really tell that story. It’s quite clouded because the test depends on avoiding the execution of parts of the code. We could add an assertion like Assertions.assertTrue(true, "expected exception occurred") in the catch block, but that doesn’t seem much better.

JUnit has a built-in mechanism to handle exceptions:

 class​ LogbookTest {
 
  @Test
»void​ readLogbook() ​throws​ IOException {
  Logbook logbook = ​new​ Logbook();
 
  List<String> entries = logbook.readAllEntries();
 
  Assertions.assertEquals(13, entries.size());
  }
 
  @Test
 void​ readLogbookFail() {
  Logbook logbook = ​new​ Logbook();
 
  Executable when = () -> logbook.readAllEntries();
 
» Assertions.assertThrows(IOException.class, when);
  }
 }

The first test looks very much like the basic test that it is. Each JUnit test contains the implicit assertion that no exception occurs. We don’t have to add that assertion explicitly—just let JUnit do its job. This really simplifies the test code, since we can remove the trycatch block and the call to fail() completely. The code reads nicer now, and we don’t break the cause chain anymore. Whenever an exception occurs, JUnit will display the complete stack trace on failure.

In the second test, we use assertThrows(). This assertion explicitly marks that we expect a specific kind of exception to be thrown in the test. Looking at the code, you can see that there’s no trycatch block anymore and no call to fail().

Instead, everything’s handled by assertThrows(). All we need to do is to pass the method that we expect to trigger the exception into the assertion. That’s something of type Executable for JUnit5. The traditional way of doing this would be to create an anonymous class (new Executable(){...}). Instead, we’d like to point ahead a little because we already applied Favor Lambdas Over Anonymous Classes above as it makes for a much more readable alternative to anonymous classes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.148.113.229