From: Eric Sosman on
On 3/19/2010 1:40 AM, Daniel Pitts wrote:
> On 3/18/2010 5:44 PM, Eric Sosman wrote:
>> [...]
>> Sorry if I wasn't clear: By referring to the "pair," I meant
>> trying to test that x.equals(y) -> x.hashCode() == y.hashCode(),
>> or that x.hashCode() != y.hashCode() -> !x.equals(y). Testing
>> equals() alone may make sense. Testing hashCode() alone almost
>> certainly doesn't. The implications must hold, but I'm not
>> convinced testing is the right way to verify that they do.
>>
> if your objects behavior depends on correct functioning of that method
> pair, then you should definitely unit test them.

Usually it's not the object's own behavior, but the
behavior of a HashMap or similar that might use your object
as a key, possibly in some circumstance you didn't anticipate.

> At the very least, testing the first is important (x.equals(y) ->
> x.hashCode() == y.hashCode()).

Yeah, but how do you test it and have confidence? You can
create a bunch of equal objects and compare hashCodes, but how
do you know you've covered the space of all glitches? For
example, you might generate the String "Hello" as a literal
and again by assembling it in a StringBuilder, and find that
the two Strings' hashCodes are equal; that's fine. But what if
they had been six characters long instead of five, or if they'd
had some other genesis?

"How do you know when you've tested enough?" is, of course,
a question that besets all testing. But with respect to the
equals/hashCode pair, I suggest that code inspection is a
superior approach, because it can lead to complete confidence
instead of to "Well, I've never seen it misbehave." If you
can show that

1) The result of the equals() method depends only on the
instance variables of the two objects, and not on any
"external" datum (once class identity is satisfied), and

2) The result of the hashCode() method depends only on the
instance variables of one instance, and

3) The variables used by hashCode() are a subset of those
used by equals()

.... then you've *proved* the methods consistent. I submit that
a solid proof is preferable to anything testing can yield, and
that these methods are nearly always simple enough to make
proof not merely feasible but straightforward.

> Actually, if you test that for all edge/general cases,

Knowing where the edge cases are requires knowledge of the
way the methods are implemented, or at the very least an informed
guess about the implementation. If you're willing to examine the
code to guide the writing of the test, it seems silly not to go
ahead and do the proof.

Ah, well: Different strokes for different folks.

> then the second
> one is probably irrelevant (or redundant)

The two implications are logically equivalent. There's an
old story about two friends who set out to test "All crows are
black." The first spent a lot of time and money, traveled the
wide world over looking for crows, and recorded that every crow
he saw was black. "Testing can't *prove* crowblackness," he said,
"but by examining a large sample of crows and finding all of them
black I have increased my level of confidence in the proposition."

The second guy was a logician, and observed that the statement
"All non-black things are non-crows" is logically equivalent to
the original: Prove either, and you have proved the other. So he
just sat back in his easy chair and let his eyes wander to non-black
things in his field of view, observing that every non-black thing he
saw was a non-crow. "Testing can't *prove* nonblacknoncrowness,"
he said, "but by examining a large sample of non-black things and
finding all of them to be non-crows I have increased my level of
confidence in both propositions. Plus, I've saved a lot of time
and money and have been lavishing it on my absent friend's main
squeeze, to our considerable enjoyment."

More prosaically, one of the implications might be easier to
test than the other.

--
Eric Sosman
esosman(a)ieee-dot-org.invalid
From: Patricia Shanahan on
Graham Cox wrote:
> Rhino <no.offline.contact.please(a)example.com> writes:
>
>> I've been lucky enough not to get burned by one of those yet - at least as
>> far as I know! - but you make a good point. Murphy's Laws, right? Anything
>> that can go wrong, will go wrong.
>>
>> It probably makes sense to test even really trivial-looking code....
>>
> I tend to think that any function with more than 2 lines of code should be
> unit tested. This means that getters and setters are generally exempt *unless
> they do something*. Specifically it means that the setters that do data
> validation first should be tested, but the setters that do nothign more than
> store the value to a member variable can be ignored...
>
>

I use different criteria. The Eclipse "Source" code generation seems to
be well tested. If I merely told Eclipse to generate a setter or getter,
or the equals/hashCode pair, I don't write a test. If I wrote a getter
or setter myself, it needs testing. I'm fully capable of making a
mistake in a single line of code.

Patricia
From: bugbear on
Patricia Shanahan wrote:
> I'm fully capable of making a
> mistake in a single line of code.

Now you're just bragging ;-)

BugBear
From: Graham Cox on
Patricia Shanahan <pats(a)acm.org> writes:

> I use different criteria. The Eclipse "Source" code generation seems to
> be well tested. If I merely told Eclipse to generate a setter or getter,
> or the equals/hashCode pair, I don't write a test. If I wrote a getter
> or setter myself, it needs testing. I'm fully capable of making a
> mistake in a single line of code.

Fair point, but I was more getting at the fact that I *do* unit test
generated functions like equals and hashCode, simply because the class
often changes after they were generated and they are no longer
valid. Just because the code was correct at the time of writing
doesn't mean it will always remain so...

--
Graham Cox
From: Tom Anderson on
On Fri, 19 Mar 2010, Eric Sosman wrote:

> "How do you know when you've tested enough?" is, of course, a
> question that besets all testing. But with respect to the
> equals/hashCode pair, I suggest that code inspection is a superior
> approach, because it can lead to complete confidence instead of to
> "Well, I've never seen it misbehave."

How often do you propose to do this inspection, and how you propose to
ensure that it is done?

tom

--
I thought it would be fun if I could get the baby do do communism stuff
with me, but when I tried to get her attention she crawled over to the
VCR and put a breadstick into it. That's not communism! That's anarchy! --
Philippe