Test a big object for equality

Một phần của tài liệu Manning JUnit recipes practical methods for program (Trang 94 - 97)

Problem

You have a Value Object with many (say more than six) key properties.14 You have tried writing a test with EqualsTester or EqualsHashCodeTestCase, but that test seems inadequate.

Background

GSBase’s EqualsTester (see recipe 2.1, “Test your equals method”) takes four parameters: two objects that ought to be different (not the same object) but equal, a third object that ought not to be equal to the first, and a fourth object—a subclass of the first object’s class—that also ought not to be equal to the first.

While this is effective for most business purposes, you may need a more thorough test, with n+3 objects: two that are equal, n that are different from those two and the last one which is a subclass. Here, n is the number of key properties in your

14By key properties we mean those properties of a Value Object for which different values mean the objects are no longer equal. The concept is analogous to the object’s primary key if it were a row in a database.

64 CHAPTER 2 Elementary tests

Value Object class. The EqualsHashCodeTestCase found in JUnit-addons suffers the same problem, because it only operates on two unequal instances of your Value Object class.

Recipe

It sounds like we need to generalize the equals testing concept to operate on an arbitrary collection of objects that ought not to be equal from the “control object.” To that end we have added this testing utility to Diasparsoft Toolkit under the name ValueObjectEqualsTest. The central algorithm has been shamelessly taken from JUnit-addons, with permission of course, and generalized to check each different way that a Value Object can be different from another. First, let us look at how to use ValueObjectEqualsTest. You can find an example—albeit a stultifyingly abstract and meaningless one15—in listing 2.9. After you subclass Val- ueObjectEqualsTest, you implement three methods.

package com.diasparsoftware.java.lang.test;

import java.util.*;

import com.diasparsoftware.util.junit.ValueObjectEqualsTest;

public class ValueObjectEqualsTestFivePropertiesTest extends ValueObjectEqualsTest {

protected List keyPropertyNames() { return Arrays.asList(

new String[] { "key1", "key2", "key3", "key4", "key5" });

}

protected Object createControlInstance() throws Exception { return new FiveKeys(1, 2, 3, 4, 5);

}

protected Object createInstanceDiffersIn(String keyPropertyName) throws Exception {

if ("key1".equals(keyPropertyName)) return new FiveKeys(6, 2, 3, 4, 5);

else if ("key2".equals(keyPropertyName)) return new FiveKeys(1, 6, 3, 4, 5);

else if ("key3".equals(keyPropertyName)) return new FiveKeys(1, 2, 6, 4, 5);

15 Sorry about that.

Listing 2.9 Using ValueObjectEqualsTest with five key properties

The names of the key properties

Each object is different from the control in the value of that key property

65 Test a big object for equality

else if ("key4".equals(keyPropertyName)) return new FiveKeys(1, 2, 3, 6, 5);

else if ("key5".equals(keyPropertyName)) return new FiveKeys(1, 2, 3, 4, 6);

return null;

} }

Each value object is defined by a set of key properties, the properties that make two instances of the value object unequal. It is common for all the properties of a value object to be its key properties, but it is not necessary. To provide the equals test with the names of those properties, implement keyPropertyNames(), to return a list of the key property names. The order in which you return them is not impor- tant. We arbitrarily decided to return them in alphabetical order.

Just like EqualsHashCodeTestCase, you need to define a “control” instance: the object against which the others will be compared for equality. Implement create- ControlInstance() to return a new object each time. Which object you decide to return is arbitrary, but that choice dictates how you implement the remaining required method. We chose the sample values 1, 2, 3, 4, and 5 for our control object.

Just like EqualsHashCodeTestCase, you need to implement the last method to return objects that are different from the control object. When you implement createInstanceDiffersBy(String keyPropertyName), you must return an object that differs from the control object in the specified key property. In our example, when the test asks for an instance that differs in the key1 property, we return the values 6, 2, 3, 4, 5, where the first key (key1, get it?) is different from the first key of the control object. From there, we imagine you see the pattern.

Discussion

If you want your equals test to verify more than just a few simple cases, you can build a Parameterized Test Case (see recipe 4.8, “Build a data-driven test suite”) from scratch, in which each test accepts three parameters: two objects and a bool- ean flag to indicate whether the parameters ought to be equal. The resulting test defines equals() using the technique known as specification by example. You specify how equals() ought to behave purely through examples: each saying, “This object and that object should be equal, but this object and that object over there should not be equal.” If you provide enough examples, you eventually arrive at a point where only a few sensible implementations of equals() work, of which any one is

66 CHAPTER 2 Elementary tests

likely suitable. Any time the equals() method returns the wrong value, you can determine which objects uncovered the defect and add them to the test to pre- vent those problems from recurring.

Related

■ 2.1—Test your equals method

■ 4.8—Build a data-driven test suite

Một phần của tài liệu Manning JUnit recipes practical methods for program (Trang 94 - 97)

Tải bản đầy đủ (PDF)

(753 trang)