Chapter 27
Value Patterns

Patterns in This Chapter

Literal Value 714

Derived Value 718

Generated Value 723

Dummy Object 728

Literal Value

Also known as

Hard-Coded Value, Constant Value

How do we specify the values to be used in tests?

We use literal constants for object attributes and assertions.

BigDecimal  expectedTotal  =  new  BigDecimal("99.95");

The values we use for the attributes of objects in our test fixture and the expected outcome of our test are often related to one another in a way that is defined in the requirements. Getting these values—and, in particular, the relationship between the pre-conditions and the post-conditions—right is crucial because it drives the correct behavior into the SUT.

Literal Values are a popular way to specify the values of attributes of objects in a test.

How It Works

We use a literal constant of the appropriate type for each attribute of an object or for use as an argument of a method call to the SUT or an Assertion Method (page 362). The expected values are calculated by hand, calculator, or spreadsheet and hard-coded within the test as Literal Values.

When to Use It

Using a Literal Value in-line makes it very clear which value is being used; there is no doubt about the value's identity because it is right in front of our face. Unfortunately, using Literal Values can make it difficult to see the relationships between the values used in various places in the test, which may in turn lead to Obscure Tests (page 186). It certainly makes sense to use Literal Values if the testing requirements specify which values are to be used and we want to make it clear that we are, in fact, using those values. [We might sometimes consider using a Data-Driven Test (page 288) instead to avoid the effort and transcription errors associated with copying the data into test methods.]

One downside of using a Literal Value is that we might use the same value for two unrelated attributes; if the SUT happens to use the wrong one, tests may pass even though they should not. If the Literal Value is a filename or a key used to access a database, the meaning of the value is lost—the content of the file or record actually drives the behavior of the SUT. Using a Literal Value as the key does nothing to help the reader understand the test in such a case, and we are likely to suffer from Obscure Tests.

If the values in the expected outcome can be derived from the values in the fixture setup logic, we will be more likely to use the Tests as Documentation (see page 23) if we use Derived Values (page 718). Conversely, if the values are not important to the specification of the logic being tested, we should consider using Generated Values (page 723).

Implementation Notes

The most common way to use a Literal Value is with literal constants within the code. When the same value needs to be used in several places in the test (typically during fixture setup and result verification), this approach can obscure the relationship between the test pre-conditions and post-conditions. Introducing an evocatively named symbolic constant can make this relationship much clearer. Likewise, if we cannot use a self-describing value, we can still make the code easier to use by defining a suitably named symbolic constant and using it wherever we would have used the Literal Value.

Variation: Symbolic Constant

When we need to use the same Literal Value in several places in a single Test Method (page 348) or within several distinct tests, it is a good practice to use a Symbolic Constant instead of a Literal Value. A Symbolic Constant is functionally equivalent to a Literal Value but reduces the likelihood of High Test Maintenance Cost (page 265).

Variation: Self-Describing Value

When several attributes of an object need the same kind of value, using different values provides advantages by helping us to prove that the SUT is working with the correct attribute. When an attribute or argument is an unconstrained string, it can be useful to choose a value that describes the role of the value in the test (a Self-Describing Value). For example, using "Not an existing customer" for the name of a customer might be more helpful to the reader than using "Joe Blow," especially when we are debugging or when the attributes are included in the test failure output.

Example: Literal Value

Because Literal Value is usually the starting point when writing tests, I'll dispense with a motivating example and cut straight to the chase. Here's an example of the Literal Value pattern in action. Note the use of Literal Values in both the fixture setup logic and the assertion.

public  void  testAddItemQuantity_1()  throws  Exception  {
      Product  product  =  new  Product("Widget",  19.95);
      Invoice  invoice  =  new  Invoice();
      //  Exercise
      invoice.addItemQuantity(product,  1);
      //  Verify
      List  lineItems  =  invoice.getLineItems();
      LineItem  actualItem  =  (LineItem)lineItems.get(0);
      assertEquals(new  BigDecimal("19.95"),
                          actualItem.getExtendedPrice());
}

The Product constructor requires both a name and a cost. The assertion on the extendedCost of the lineItem requires a value for the total cost of the product for that line item. In this example, we included these values as hard-coded literal constants. In the next example, we'll use symbolic constants instead.

Refactoring Notes

We can reduce the Test Code Duplication (page 213) in the form of the hard-coded Literal Value of 19.95 by doing a Replace Magic Number with Symbolic Constant [Fowler] refactoring.

Example: Symbolic Constant

This refactored version of the original test replaces the duplicated Literal Value of the widget's price (19.95) with a suitably named Symbolic Constant that is used during fixture setup as well as result verification:

public  void  testAddItemQuantity_1s()  throws  Exception  {
      BigDecimal  widgetPrice  =  new  BigDecimal("19.95");
      Product  product  =  new  Product("Widget",  widgetPrice);
      Invoice  invoice  =  new  Invoice();
      //  Exercise
      invoice.addItemQuantity(product,  1);
      //  Verify
      List  lineItems  =  invoice.getLineItems();
      LineItem  actualItem  =  (LineItem)lineItems.get(0);
      assertEquals(widgetPrice,  actualItem.getExtendedPrice());
}

Example: Self-Describing Value

This refactored version of the test provides a Self-Describing Value for the mandatory name argument passed to the Product constructor. This value is not used by the method we are testing; it is merely stored for later access by another method we are not testing here.

public  void  testAddItemQuantity_1b()  throws  Exception  {
      BigDecimal  widgetPrice  =  new  BigDecimal("19.95");
      Product  product  =  new  Product("Irrelevant  product  name",
                                                           widgetPrice);
      Invoice  invoice  =  new  Invoice();
      //  Exercise
      invoice.addItemQuantity(product,  1);
      //  Verify
      List  lineItems  =  invoice.getLineItems();
      LineItem  actualItem  =  (LineItem)lineItems.get(0);
      assertEquals(widgetPrice,  actualItem.getExtendedPrice());
}

Example: Distinct Value

This test needs to verify that the item's name is taken from the product's name. We'll use a Distinct Value for the name and the SKU so we can tell them apart.

public  void  testAddItemQuantity_1c()  throws  Exception  {
      BigDecimal  widgetPrice  =  new  BigDecimal("19.95");
      String  name  =  "Product  name";
      String  sku  =  "Product  SKU";
      Product  product  =  new  Product(name,  sku,  widgetPrice);
      Invoice  invoice  =  new  Invoice();
      //  Exercise
      invoice.addItemQuantity(product,  1);
      //  Verify
      List  lineItems  =  invoice.getLineItems();
      LineItem  actualItem  =  (LineItem)lineItems.get(0);
      assertEquals(name,  actualItem.getName());
}

This also happens to be an example of a self-describing value.

Derived Value

Also known as

Calculated Value

How do we specify the values to be used in tests?

We use expressions to calculate values that can be derived from other values.

BigDecimal  expectedTotal  =  itemPrice.multiply(QUANTITY);

The values we use for the attributes of objects in our test fixtures and the result verification parts of our tests are often related to one another in a way that is defined in the requirements. Getting these values—and, in particular, the relationship between the pre-conditions and the post-conditions—right is crucial because it drives the correct behavior into the SUT and helps the tests act as documentation of our software.

Often, some of these values can be derived from other values in the same test. In these cases the benefits from using our Tests as Documentation (see page 23) are improved if we show the derivation by calculating the values using the appropriate expression.

How It Works

Computers are really good at math and string concatenation. We can avoid doing the math in our head (or with a calculator) by coding the math for expected results as arguments of the Assertion Method (page 362) calls directly into the tests. We can also use Derived Values as arguments for fixture object creation and as method arguments when exercising the SUT.

Derived Values, by their very nature, encourage us to use variables or symbolic constants to hold the values. These variables/constants can be initialized at compile time (constants), during class or Testcase Object (page 382) initialization, during fixture setup, or within the body of the Test Method (page 348).

When to Use It

We should use a Derived Value whenever we have values that can be derived in some deterministic way from other values in our tests. The main drawback of using Derived Values is that the same math error (e.g., rounding errors) could appear in both the SUT and the tests. To be safe, we might want to code a few of the pathological test cases using Literal Values (page 714) just in case such a problem might be present. If the values we are using must be unique or don't affect the logic in the SUT, we may be better off using Generated Values (page 723) instead.

We can use a Derived Value either as part of fixture setup (Derived Input or One Bad Attribute) or when determining the expected values to be compared with those generated by the SUT (Derived Expectation). These uses are described in a bit more detail later in this section.

Variation: Derived Input

Sometimes our test fixture contains similar values that the SUT might compare or use to base its logic on the difference between them. For example, a Derived Input might be calculated in the fixture setup portion of the test by adding the difference to a base value. This operation makes the relationship between the two values explicit. We can even put the value to be added in a symbolic constant with an Intent-Revealing Name [SBPP] such as MAXIMUM_ALLOWABLE_TIME_DIFFERENCE.

Variation: One Bad Attribute

A Derived Input is often employed when we need to test a method that takes a complex object as an argument. For example, thorough "input validation" testing requires that we exercise the method with each of the attributes of the object set to one or more possible invalid values to ensure that it handles all of these cases correctly. Because the first rejected value could cause termination of the method, we must verify each bad attribute in a separate call to the SUT; each of these calls, in turn, should be done in a separate test method (each should be a Single-Condition Test; see page 45). We can instantiate the invalid object easily by first creating a valid object and then replacing one of its attributes with an invalid value. It is best to create the valid object using a Creation Method (page 415) so as to avoid Test Code Duplication (page 213).

Variation: Derived Expectation

When some value produced by the SUT should be related to one or more of the values we passed in to the SUT as arguments or as values in the fixture, we can often derive the expected value from the input values as the test executes rather than using precalculated Literal Values. We then use the result as the expected value in an Equality Assertion (see Assertion Method).

Motivating Example

The following test doesn't use Derived Values. Note the use of Literal Values in both the fixture setup logic and the assertion.

public  void  testAddItemQuantity_2a()  throws  Exception  {
      BigDecimal  widgetPrice  =  new  BigDecimal("19.99");

      Product  product  =  new  Product("Widget",  widgetPrice);
      Invoice  invoice  =  new  Invoice();
      //  Exercise
      invoice.addItemQuantity(product,  5);
      //  Verify
      List  lineItems  =  invoice.getLineItems();
      LineItem  actualItem  =  (LineItem)lineItems.get(0);
      assertEquals(new  BigDecimal("99.95"),
                           actualItem.getExtendedPrice());
}

Test readers may have to do some math in their heads to fully appreciate the relationship between the values in the fixture setup and the value in the result verification part of the test.

Refactoring Notes

To make this test more readable, we can replace any Literal Values that are actually derived from other values with formulas that calculate these values.

Example: Derived Expectation

The original example contained only one line item for five instances of the product. We therefore calculated the expected value of the extended price attribute by multiplying the unit price by the quantity, which makes the relationship between the values explicit.

public  void  testAddItemQuantity_2b()  throws  Exception  {
      BigDecimal  widgetPrice  =  new  BigDecimal("19.99");
      BigDecimal  numberOfUnits  =  new  BigDecimal("5");
      Product  product  =  new  Product("Widget",  widgetPrice);
      Invoice  invoice  =  new  Invoice();
      //  Exercise
      invoice.addItemQuantity(product,  numberOfUnits);
      //  Verify
      List  lineItems  =  invoice.getLineItems();
      LineItem  actualItem  =  (LineItem)lineItems.get(0);
      BigDecimal  totalPrice  =  widgetPrice.multiply(numberOfUnits);
      assertEquals(totalPrice,  actualItem.getExtendedPrice());
}

Note that we have also introduced symbolic constants for the unit price and quantity to make the expression even more obvious and to reduce the effort of changing the values later.

Example: One Bad Attribute

Suppose we have the following Customer Factory Method [GOF], which takes a CustomerDto object as an argument. We want to write tests to verify what occurs when we pass in invalid values for each of the attributes in the CustomerDto. We could create the CustomerDto in-line in each Test Method with the appropriate attribute initialized to some invalid value.

public  void  testCreateCustomerFromDto_BadCredit()  {
        //  fixture  setup
        CustomerDto  customerDto  =  new  CustomerDto();
        customerDto.firstName  =  "xxx";
        customerDto.lastName  =  "yyy";
        //  etc.
        customerDto.address  =  createValidAddress();
        customerDto.creditRating  =  CreditRating.JUNK;
        //  exercise  the  SUT
        try  {
              sut.createCustomerFromDto(customerDto);
              fail("Expected  an  exception");
        }  catch  (InvalidInputException  e)  {
              assertEquals(  "Field",  "Credit",  e.field  );
        }
}

public  void  testCreateCustomerFromDto_NullAddress()  {
        //  fixture  setup
        CustomerDto  customerDto  =  new  CustomerDto();
        customerDto.firstName  =  "xxx";
        customerDto.lastName  =  "yyy";
        //  etc.
        customerDto.address  =  null;
        customerDto.creditRating  =  CreditRating.AAA;
        //  exercise  the  SUT
        try  {
              sut.createCustomerFromDto(customerDto);
              fail("Expected  an  exception");
        }  catch  (InvalidInputException  e)  {
              assertEquals(  "Field",  "Address",  e.field  );
        }
}

The obvious problem with this code is that we end up with a lot of Test Code Duplication because we need at least one test per attribute. The problem becomes even worse if we are doing incremental development: We will require more tests for each newly added attribute, and we will have to revisit all existing tests to add the new attribute to the Factory Method signature.

The solution is to define a Creation Method that produces a valid instance of the CustomerDto (by doing an Extract Method [Fowler] refactoring on one of the tests) and uses it in each test to create a valid DTO. Then we simply replace one of the attributes with an invalid value in each of the tests. Each test now has an object with One Bad Attribute, with each one invalid in a slightly different way.

public  void  testCreateCustomerFromDto_BadCredit_OBA()  {
      CustomerDto  customerDto  =  createValidCustomerDto();
      customerDto.creditRating  =  CreditRating.JUNK;
      try  {
            sut.createCustomerFromDto(customerDto);
            fail("Expected  an  exception");
        }  catch  (InvalidInputException  e)  {
            assertEquals(  "Field",  "Credit",  e.field  );
        }
}

public  void  testCreateCustomerFromDto_NullAddress_OBA()  {
      CustomerDto  customerDto  =  createValidCustomerDto();
      customerDto.address  =  null;
      try  {
            sut.createCustomerFromDto(customerDto);
            fail("Expected  an  exception");
      }  catch    (InvalidInputException  e)  {
            assertEquals(  "Field",  "Address",  e.field  );
      }
}

Generated Value

How do we specify the values to be used in tests?

We generate a suitable value each time the test is run.
BigDecimal  uniqueCustomerNumber  =  getUniqueNumber();

When initializing the objects in the test fixture, one issue that must be dealt with is the fact that most objects have various attributes (fields) that need to be supplied as arguments to the constructor. Sometimes the exact values to be used affect the outcome of the test. More often than not, however, it is important only that each object use a different value. When the precise values of these attributes are not important to the test, it is important not to have them visible within the test!

Generated Values are used in conjunction with Creation Methods (page 415) to help us remove this potentially distracting information from the test.

How It Works

Instead of deciding which values to use in our tests while we are coding the tests, we generate the values when we actually execute the tests. We can then pick values to satisfy specific criteria such as "must be unique in the database" that can be determined only as the test run unfolds.

When to Use It

We use a Generated Value whenever we cannot or do not want to specify the test values until the test is executing. Perhaps the value of an attribute is not expected to affect the outcome of the test and we don't want to be bothered to define Literal Values (page 714), or perhaps we need to ensure some quality of the attribute that can be determined only at runtime. In some cases, the SUT requires the value of an attribute to be unique; using a Generated Value can ensure that this criterion is satisfied and thereby prevent Unrepeatable Tests (see Erratic Test on page 228) and Test Run Wars (see Erratic Test) by reducing the likelihood of a test conflicting with its parallel incarnation in another test run. Optionally, we can use this distinct value for all attributes of the object; object recognition then becomes very easy when we inspect the object in a debugger.

One thing to be wary of is that different values could expose different bugs. For example, a single-digit number may be formatted correctly, whereas a multidigit number might not (or vice versa). Generated Values can result in Nondeterministic Tests (see Erratic Test); if we encounter nondeterminism (sometimes the test passes and then fails during the very next run), we must check the SUT code to see whether differences in value could be the root cause.

In general, we shouldn't use a Generated Value unless the value must be unique because of the nondeterminism such a value may introduce. The obvious alternative is to use a Literal Value. A less obvious alternative is to use a Derived Value (page 718), especially when we must determine the expected results of a test.

Implementation Notes

We can generate values in a number of ways. The appropriateness of each technique depends on the circumstance.

Variation: Distinct Generated Value

When we need to ensure that each test or object uses a different value, we can take advantage of Distinct Generated Values. In such a case, we can create a set of utility functions that will return unique values of various types (e.g., integers, strings, floating-point numbers). The various getUnique methods can all be built upon an integer sequence number generator. For numbers that must be unique within the scope of a shared database, we can use database sequences or a sequence table. For numbers that must be unique within the scope of a particular test run, we can use an in-memory sequence number generator (e.g., use a Java static variable that is incremented before usage). In-memory sequence numbers that start from the number 1 each time a test suite is run offer a useful quality: The values generated in each test are the same for each run and can simplify debugging.

Variation: Random Generated Value

One way to obtain good test coverage without spending a lot of time analyzing the behavior and generating test conditions is to use different values each time we run the tests. Using a Random Generated Value is one way to accomplish this goal. While use of such values may seem like a good idea, it makes the tests nondeterministic (Nondeterministic Tests) and can make debugging failed tests very difficult. Ideally, when a test fails, we want to be able to repeat that test failure on demand. To do so, we can log the Random Generated Value as the test is run and show it as part of the test failure. We then need to find a way to force the test to use that value again while we are troubleshooting the failed test. In most cases, the effort required outweighs the potential benefit. Of course, when we need this technique, we really need it.

Variation: Related Generated Value

An optional enhancement is to combine a Generated Value with a Derived Value by using the same generated integer as the root for all attributes of a single object. This result can be accomplished by calling getUniqueInt once and then using that value to build unique strings, floating-point numbers, and other values. With a Related Generated Value, all fields of the object contain "related" data, which makes the object easier to recognize when debugging. Another option is to separate the generation of the root from the generation of the values by calling generateNewUniqueRoot explicitly before calling getUniqueInt,  getUniqueString, and so on.

Another nice touch for strings is to pass a role-describing argument to the function that is combined with the unique integer key to make the code more intent-revealing. Although we could also pass such arguments to the other functions, of course we wouldn't be able to build them into an integer value.

Motivating Example

The following test uses Literal Values for the arguments to a constructor:

public  void  testProductPrice_HCV()  {
      //      Setup
      Product  product  =
            new  Product(  88,                                          //  ID
                                    "Widget",                                //  Name
                                      new  BigDecimal("19.99"));  //  Price
      //  Exercise
      //      ...
}

Refactoring Notes

We can convert the test to use Distinct Generated Values by replacing the Literal Values with calls to the appropriate getUnique method. These methods simply increment a counter each time they are called and use that counter value as the root for construction of an appropriately typed value.

Example: Distinct Generated Value

Here is the same test using a Distinct Generated Value. For the getUniqueString method, we'll pass a string describing the role ("Widget Name").

public  void  testProductPrice_DVG()  {
    //  Setup
    Product  product  =
    new  Product(  getUniqueInt(),                         //  ID
                            getUniqueString("Widget"),  //  Name
                            getUniqueBigDecimal());      //  Price
    //  Exercise
    //      ...
}

static  int  counter  =  0;

int  getUniqueInt()  {
      counter++;
      return  counter;
}

BigDecimal  getUniqueBigDecimal()  {
      return  new  BigDecimal(getUniqueInt());
}

String  getUniqueString(String  baseName)  {
      return  baseName.concat(String.valueOf(  getUniqueInt()));
}

This test uses a different generated value for each argument of the constructor call. The numbers generated in this way are consecutive but the test reader still needs to look at a specific attribute when debugging to get a consistent view. We probably should not generate the price value if the logic we were testing was related to price calculation because that would force our verification logic to accommodate different total costs.

Example: Related Generated Value

We can ensure that all values used by the test are obviously related by separating the generation of the root value from the construction of the individual values. In the following example, we've moved the generation of the root to the setUp method so each test method gets a new value only once. The methods that retrieve the various values (e.g., getUniqueString) simply use the previously generated root when deriving the Generated Values.

public  void  testProductPrice_DRVG()  {
      //        Setup
      Product  product  =
            new  Product(  getUniqueInt(),                        //  ID
                                    getUniqueString("Widget"),    //  Name
                                    getUniqueBigDecimal());        //  Price
        //  Exercise
        //      ...
}

static  int  counter  =  0;

public  void  setUp()  {
      counter++;
}

int  getUniqueInt()  {
      return  counter;
}

String  getUniqueString(String  baseName)  {
      return  baseName.concat(String.valueOf(  getUniqueInt()));
}

BigDecimal  getUniqueBigDecimal()  {
      return  new  BigDecimal(getUniqueInt());
}

If we looked at this object in an object inspector or database or if we dumped part of it to a log, we could readily tell which object we were looking at regardless of which field we happened to see.

Dummy Object

Also known as

Dummy, Dummy Parameter, Dummy Value, Placeholder, Stub

How do we specify the values to be used in tests when the only usage is as irrelevant arguments of SUT method calls?

We pass an object that has no implementation as an argument of a method called on the SUT.

Invoice  inv  =  new  Invoice(  new  DummyCustomer()  );

Getting the SUT into the right state to start a test often requires calling other methods of the SUT. These methods commonly take as arguments objects that are stored in instance variables for later use. Often, these objects (or at least some attributes of these objects) are never used in the code that we are actually testing. Instead, we create them solely to conform to the signature of some method we must call to get the SUT into the right state. Constructing these objects can be nontrivial and adds unnecessary complexity to the test.

In these cases, a Dummy Object can be passed as an argument, eliminating the need to build a real object.

How It Works

We create an instance of some object that can be instantiated easily and with no dependencies; we then pass that instance as the argument of the method of the SUT. Because it won't actually be used within the SUT, we don't need any implementation for this object. If any of the methods of the Dummy Object are invoked, the test really should throw an error. Trying to invoke a nonexistent method will typically produce that result.

When to Use It

We can use Dummy Objects whenever we need to use objects as attributes of other objects or arguments of methods on the SUT or other fixture objects. Using Dummy Objects helps us avoid Obscure Tests (page 186) by leaving out the irrelevant code that would be necessary to build real objects and by making it clear which objects and values are not used by the SUT.

If we need to control the indirect inputs or verify the indirect outputs of the SUT, we should probably use a Test Stub (page 529) or a Mock Object (page 544) instead. If the object will be used by the SUT but we cannot provide the real object, we should consider providing a Fake Object (page 551) that provides just enough behavior for the test to execute.

We can use one of the value patterns when the SUT really does need to use the object in some way. Either a Literal Value (page 714), a Generated Value (page 723), or a Derived Value (page 718) may be appropriate, depending on the circumstance.

Variation: Dummy Argument

We can use a Dummy Argument whenever methods of the SUT take objects as arguments1 and those objects are not relevant to the test.

Variation: Dummy Attribute

We can use a Dummy Attribute whenever we are creating objects that will be used as part of the fixture or as arguments of SUT methods, and some of the attributes of those objects are not relevant to the test.

Implementation Notes

The simplest implementation of a Dummy Object is to pass a null value as the argument. This approach works even in a statically typed language such as Java, albeit only if the method being called doesn't check for null arguments. If the method complains when we pass it null, we'll need to employ a slightly more sophisticated implementation. The biggest disadvantage to using null is that it is not very descriptive.

In dynamically typed languages such as Ruby, Perl, and Python, the actual type of the object will never be checked (because it will never be used), so we can use any class such as String or Object. In such a case, it is useful to give the object a Self-Describing Value (see Literal Value) such as "Dummy Customer."

In statically typed languages (such as Java, C#, and C++), we must ensure that the Dummy Object is type compatible with the parameter it is to match. Type compatibility is much easier to achieve if the parameter has an abstract type (e.g., an Interface in Java) because we can create our own trivial implementation of the type or pass a suitable Pseudo-Object (see Hard-Coded Test Double on page 568). If the parameter type is a concrete class, we may be able to create a trivial instance of it or we may need to create an instance of a Test-Specific Subclass (page 579) within our test.

Some Mock Object frameworks have Test Utility Methods (page 599) that will generate a Dummy Object for a specified class that takes a String argument for a Self-Describing Value.

While the Dummy Object may, in fact, be null, it is not the same as a Null Object [PLOPD3]. A Dummy Object is not used by the SUT, so its behavior is either irrelevant or it should throw an exception when executed. In contrast, a Null Object is used by the SUT but is designed to do nothing. That's a small but very important distinction!

Motivating Example

In this example, we are testing the Invoice but we require a Customer to instantiate the invoice. The Customer requires an Address, which in turn requires a City. Thus we find ourselves creating several additional objects just to set up the fixture. But if we know that the behavior we are testing should not access the Customer at all, why do we need to create it and all the objects on which it depends?

public  void  testInvoice_addLineItem_noECS()  {
      final  int  QUANTITY  =  1;
      Product  product  =  new  Product(getUniqueNumberAsString(),
                                                            getUniqueNumber());
      State  state  =  new  State("West  Dakota",  "WD");
      City  city  =  new  City("Centreville",  state);
      Address  address  =  new  Address("123  Blake  St.",  city,  "12345");
      Customer  customer=  new  Customer(getUniqueNumberAsString(),
                                                                 getUniqueNumberAsString(),
                                                                  address);
      Invoice  inv  =  new  Invoice(customer);
      //  Exercise
      inv.addItemQuantity(product,  QUANTITY);
      //  Verify
      List  lineItems  =  inv.getLineItems();
      assertEquals("number  of  items",  lineItems.size(),  1);
      LineItem  actual  =  (LineItem)lineItems.get(0);
      LineItem  expItem  =  new  LineItem(inv,  product,  QUANTITY);
      assertLineItemsEqual("",expItem,  actual);
}

This test is quite cluttered as a result of the extra object creation. How is the behavior we are testing related to the Address and City? From this test, we can only assume that there is some relation. But this misleads the test reader!

Refactoring Notes

If the objects in the fixture are not relevant to the test, they should not be visible in the test. Therefore, we should try to eliminate the need to create all these objects. We could try passing in null for the Customer. In this case, the constructor checks for null and rejects it, so we have to find another way.

The solution is to replace the object that is not important to our test with a Dummy Object. In dynamically typed languages, we could just pass in a string. In statically typed languages such as Java and C#, however, we must pass in a type-compatible object. In this case, we have chosen to do an Extract Interface [Fowler] refactoring on Customer to create a new interface and then create a new implementation class called DummyCustomer. Of course, as part of the Extract Interface refactoring, we must replace all references to Customer with the new interface name so that the DummyCustomer will be acceptable. A less intrusive option would be to use a Test-Specific Subclass of Customer that adds a test-friendly constructor.

Example: Dummy Values and Dummy Objects

Here's the same test using a Dummy Object instead of the Product name and the Customer. Note how much simpler the fixture setup has become!

  public  void  testInvoice_addLineItem_DO()  {
        final  int  QUANTITY  =  1;
        Product  product  =  new  Product("Dummy  Product  Name",
                                                             getUniqueNumber());
        Invoice  inv  =  new  Invoice(  new  DummyCustomer()  );
        LineItem  expItem  =  new  LineItem(inv,  product,  QUANTITY);
        //  Exercise
        inv.addItemQuantity(product,  QUANTITY);
        //  Verify
        List  lineItems  =  inv.getLineItems();
        assertEquals("number  of  items",  lineItems.size(),  1);
        LineItem  actual  =  (LineItem)lineItems.get(0);
        assertLineItemsEqual("",  expItem,  actual);
}

Using a Dummy Object for the name of the Product was simple because it is a string and has no uniqueness requirement. Thus we were able to use a Self-Describing Value. We were not able to use a Dummy Object for the Product number because it must be unique, so we left it as a Generated Value. The Customer was a bit trickier because the LineItem's constructor expected a non-null object. Because this example is written in Java, the method parameter is strongly typed; for this reason, we needed to create an alternative implementation of the ICustomer interface with a no-argument constructor to simplify in-line construction. Because the DummyCustomer is never used, we have created it in-line rather than declaring a variable to hold it. This choice reduces the fixture setup code by one line, and the presence of the in-line constructor call within the call to the Invoice constructor reinforces the message that we need the Dummy Object only for the constructor call and not for the rest of the test.

Here is the code for the DummyCustomer:

  public  class  DummyCustomer  implements  ICustomer  {

        public  DummyCustomer()  {
              //  Real  simple;  nothing  to  initialize!
        }

        public  int  getZone()  {
              throw  new  RuntimeException("This  should  never  be  called!");
        }
}

We have implemented the DummyCustomer class with just those methods declared in the interface; because each method throws an exception, we know when it is hit. We could also have used a Pseudo-Object for the DummyCustomer. In other circumstances we might have been able to simply pass in null or construct a dummy instance of the real class. The major problem with the latter technique is that we won't know for sure if the Dummy Object is actually used.

Further Reading

When [UTwJ] refers to a "dummy object," these authors are referring to what this book terms a Test Stub. See Mocks, Fakes, Stubs, and Dummies in Appendix B for a more thorough comparison of the terminology used in various books and articles. The JMock and NMock frameworks for testing with Mock Objects support auto-generation of Dummy Objects.

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

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