Category: Uncategorized

  • Inheritance and contracts

    We mostly use Java for the examples and Java is an object-oriented language, so I must discuss what happens when we use inheritance. Figure 4.2 shows that the TaxCalculator class has many children (TaxCalculatorBrazil which calculates taxes in Brazil, TaxCalculatorNL, which calculates taxes in the Netherlands, and so on). These child classes all override calculateTax() and change the pre- or post-conditions…

  • Invariants

    We have seen that pre-conditions should hold before a method’s execution, and post-conditions should hold after a method’s execution. Now we move on to conditions that must always hold before and after a method’s execution. These conditions are called invariants. An invariant is thus a condition that holds throughout the entire lifetime of an object or a data…

  • Strong and weak pre- and post-conditions

    When defining pre- and post-conditions, an important decision is how weak or strong you want them to be. In the previous example, we handle the pre-condition very strongly: if a negative value comes in, it violates the pre-condition of the method, so we halt the program. One way to avoid halting the program due to…

  • The assert keyword

    The Java language offers the keyword assert, which is a native way of writing assertions. In the previous example, instead of throwing an exception, we could write assert value >= 0 : “Value cannot be negative.”. If value is not greater than or equal to 0, the Java Virtual Machine (JVM) will throw an AssertionError. In the following listing, I show a version of the TaxCalculator using asserts. Listing 4.3 TaxCalculator with…

  • Pre-conditions and post-conditions

    Going back to the tax calculation example, we need to reflect on pre-conditions that the method needs to function properly, as well as its post-conditions: what the method guarantees as outcomes. We already mentioned a pre-condition: the method does not accept negative numbers. A possible post-condition of this method is that it also does not return negative numbers. Once the…

  • Introduction

    Imagine a piece of software that handles a very complex financial process. For that big routine to happen, the software system chains calls to several subroutines (or classes) in a complex flow of information: that is, the results of one class are passed to the next class, whose results are again passed to the next…

  • Mutation testing

    How much of the production code is exercised by a test. What they all miss is whether the assertions that these tests make are good and strong enough to capture bugs. If we introduce a bug in the code, even in a line covered by a test, will the test break? As mentioned earlier, coverage…

  • What should not be covered?

    We have talked a lot about what to test and cover. Let’s quickly discuss what not to cover. Achieving 100% coverage may be impossible or not even desirable. For example, the code snippet in listing 3.12 returns the full path of a specific directory. The code may throw a URISyntaxException, which we catch and wrap around a RuntimeException. (For…

  • Other coverage criteria

    We have used the program’s control flow as a way to derive different tests. Another way of approaching structural testing is to look at the data flow: examining how the data flows to different parts of the program. For example, imagine that a variable is defined, then modified one, two, or three times in other parts of…

  • MC/DC when expressions are too complex and cannot be simplified

    MC/DC is increasingly valuable as expressions become more complicated. Listing 3.11 shows an example of a complex expression that I extracted from Chilenski’s 2001 paper. It is an anonymized version of a condition found in a level A flight simulation program and contains an impressive 76 conditions. Achieving path coverage in such a complex expression…