Describe Your Tests

 class​ OxygenTankTest {
 static​ ​final​ ​double​ PERMILLE = 0.001;
 
  @Test
» @Disabled
 void​ testFill() {
  OxygenTank smallTank = OxygenTank.withCapacity(50);
 
  smallTank.fill(22);
 
  Assertions.assertEquals(0.44, smallTank.getStatus(), PERMILLE);
  }
 
» @Test
 private​ ​void​ testFill2() {
  OxygenTank bigTank = OxygenTank.withCapacity(10_000);
  bigTank.fill(5344.0);
 
  Executable when = () -> bigTank.fill(6000);
 
  Assertions.assertThrows(IllegalArgumentException.class, when);
  }
 }

Eventually, tests will fail. That’s what they’re there for—telling you when you break something. When tests fail, the first thing you’ll see is their name. Good names are valuable—they help you find the cause for failure faster.

In the code above, we’ve already done a lot of good things: we’ve applied Structure Tests Into Given-When-Then and the previous comparisons. We even use the assertThrows() assertion that expects a specific exception to be thrown within an Executable. But the code still lacks something crucial: good names and documentation. What do the tests actually check for?

A failing test initially only shows us the class name and the name of its methods. We know that this class contains tests for the OxygenTank class because it follows the [ProductionClass]Test name convention. This is helpful, but the method names aren’t; they only list which method is being tested (the method fill()) and a test number. From this name, it’s still unclear what they actually test for (except that they test the method fill()). Sadly, you’ll find test names like these frequently in real code, because most IDEs automatically generate them using the test[MethodName] convention.

Adding comments might be one option here. But with JUnit5, there are much better solutions.

So how can we improve the test description here?

 class​ OxygenTankTest {
 static​ ​final​ ​double​ PERMILLE = 0.001;
 
  @Test
» @DisplayName(​"Expect 44% after filling 22l in an empty 50l tank"​)
  @Disabled(​"We don't have small tanks anymore! TODO: Adapt for big tanks"​)
 void​ fillTank() {
  OxygenTank smallTank = OxygenTank.withCapacity(50);
 
  smallTank.fill(22);
 
  Assertions.assertEquals(0.44, smallTank.getStatus(), PERMILLE);
  }
 
  @Test
» @DisplayName(​"Fail if fill level > tank capacity"​)
 void​ failOverfillTank() {
  OxygenTank bigTank = OxygenTank.withCapacity(10_000);
  bigTank.fill(5344.0);
 
  Executable when = () -> bigTank.fill(6000);
 
  Assertions.assertThrows(IllegalArgumentException.class, when);
  }
 }

The first thing we’ve done is to get rid of superfluous terms in the method names. They no longer start with words like test. Instead, they describe the context, the method under test, and the assertion we check. The intention in a test named failOverfillTank() is so much clearer than testFill2()! Still, those names have limits: no spaces, and only letters and numbers are allowed.[39]

With JUnit5, there’s a solution for adding good test descriptions without tweaking method names: @DisplayName("description with spaces and symbols"). With this annotation, you can use spaces and symbols, such as % or >, to write really expressive and compact test descriptions. Spaces make them readable as sentences, and signs allow short and concise descriptions of units.

Describing why a test is disabled is important, because it is your motivation for not just deleting the code. We suggest that you use the format of @Disabled("[why it’s disabled] TODO: [what’s the plan to enable again]"). This a) forces you to think about the future of this test when you disable it, and b) provides a future developer with the necessary information to enable it again (instead of leaving her high and dry without a clue).

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

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