Friday, June 19, 2009

A rose by any other name...

Martin Odersky, Lex Spoon, and Bill Venners have collaborated to venture where everyone has gone before. Yes, I mean the age old story about right way to override the equals method in Java. I see you rolling your eyes with the "I know, I know I must override hashcode method as well" bored refrain.

Spare me a few more minutes and I will convince you that you must give the article a serious once over. I for one got a better appreciation of the complexity of the problem -- if you want it addressed fully.

The article warms up around the 3rd of the four pitfalls they identify
  1. Defining equals with the wrong signature
  2. Changing equals without also changing hashCode
  3. Defining equals in terms of mutable fields
  4. Failing to define equals as an equivalence relation
Pitfall #3 discusses what happens when the state of an object that has been placed in a collection is modified. Typical equalsmethod implementation will result in it disappearing from the collection.

public class Point {
private int x, y;
public Point(int i, int j) { x = i; y = j; }
public int getX() { return x; }
public void setX(int i) { x = i; }
public int getY() { return y; }
public void setY(int i) { y = i; }
@Override public int hashCode() { return 41 * (41 + getX()) + getY(); }
@Override public boolean equals(Object other) {
boolean result = false;
if (other instanceof Point) {
Point that = (Point) other;
result = (this.getX() == that.getX() && this.getY() == that.getY());
}
return result;
}

}
..
HashSet hashSet = new HashSet();
Point p = new Point(1, 2);
hashSet.add(p);
p.setX(12);
System.out.println(hashSet.contains(p)); // <-- Prints false!!

Yep! A forehead smacking moment alright. The article suggests a way around this problem. Which leads us to the 4th pitfall -- that of equivalence. This means that for non-null values, the following is true about the equals method:
  • Reflexivity: x.equals(x)
  • Symmetry: x.equals(y) => y.equals(x)
  • Transitivity:x.equals(y) and y.equals(z) => x.equals(z)
  • Consistence: x.equals(y) always returns the same result
  • x.equals(null) is always false
With clear and concise examples, the article explains the complications of these contracts and how to implement them.

Tuesday, April 07, 2009

Run a single JUnit test method in Eclipse

Goes to show you, that you can teach an old dog new tricks. After years of using Eclipse I stumbled across a feature I am sure has existed since the glory days of Visual Age.

I had added yet another test method to a large JUnit TestCase class. I wanted to just run and re-run the new method 'til I had it just right without having to run all the other methods as well.

Yes of course, I could comment out all the other methods or just remove the @Test annotation. Back in the JUnit 3 days we have all -- at one time or another -- disabled tests by changing the "test" prefix of the test methods.

Somehow today, all these seemed abhorrent.

I wondered what would happen if I simply highlighted the method I wanted to test, and selected Run As > JUnit Test from the context menu.

Voila! That was the ticket!! It ran just the test I had wanted run.

As a bonus it even added TestCase.method_name as a "Run Configuration" for ease of future use.

FTW!

Friday, February 27, 2009

Mockito: Most helpful stacktrace

One of the things I have admired about the Spring framework is its helpful error messages. Yeah, of all the things I could point to, I picked that....However, work with me here. What I mean is that when things go wrong when using Spring, it spits out error messages that are full sentences that make sense and point the developer in the proper direction. None of the NoClassDefFoundError but not telling who which freaking class.

Now, I think Mockito -- the Java mocking framework -- has taken over the mantle of "King of error messages". Earlier today I had a new unit test I had just written fail. I didn't have to dig too much to find the error because Mockito pointed the way with the following stacktrace.

org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unifinished stubbing detected!
E.g. toReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Also make sure the method is not final - you cannot stub final methods.
at
etc. etc.

How cool is that! Hats off, Mockito-team. This developer appreciates the extra few minutes you took to put together that message. It helped me immensely.

Wednesday, February 18, 2009

Tab navigating the Mac UI

It was driving me nuts when I got my MacBook that I could not use the tab button to navigate across a form. Let alone forms, on a browser page, I could only tab between the address bar and the search field. To go anywhere else, I had to reach for the mouse. Aaargh!!

Surely the operating system that was God's gift to humanity could do better than that! It took a fair amount of Googling for me to find the obscure setting that controls it.

Here's what you need to do in order to be able to tab away across the entire screen.

Bring up the System Preferences window and select Keyboard & Mouse from the Hardware Category.



From the "Keyboard & Mouse" panel, select the "Keyboard Shortcuts" tab. The magical setting is at the bottom of that tab. Allow "Full keyboard access" by selecting the "All controls" radio button.



As this panel shows, you can impress your colleagues by toggling this setting using CTRL+Fn+F7.

The question remains, who in their right mind thought that the default setting was to be "Text boxes and lists only"!!

Friday, February 13, 2009

JUnit 3 & JUnit 4: Oil & water

Version 4 of the popular JUnit test framework has been available for use for quite some time (In fact, it is up to version 4.5). However, many projects have a wealth of JUnit 3-style tests and developers may choose to continue using it. If, on the other hand, you decide to dip your toes in JUnit 4 waters, make it a complete immersion. Don't try to mix and match.

As you may be aware, couple of the touted "benefits" of JUnit 4 are the obviation of extending the TestCase class and the identification of test and lifecycle methods (setUp and tearDown) using Java 5 annotations. For example, the following is a valid JUnit 4-style test case (minus the necessary imports):

public class BusinessLogicTests {
  @Before
  public void do_this_before_every_test() {
    // set up logic goes here
  }

  @After
  public void clean_up_after_a_test() {
    // teardown logic goes here
  }

  @Test
  public void addition() {
    assertEquals("Invalid addition", 2, 1+1);
  }
}

Observe how

  • The class does not extend TestCase

  • The "setup" and "tearDown" methods are identified by annotations

  • The test method does not require a "test" prefix and is also annotated


I'll let you be the judge of whether this is an improvement on JUnit 3. What I want to alert you to are the perils of subconciously mixing JUnit-3 and JUnit-4 style programming. Consider the following test case:

public class BusinessLogicTests extends TestCase {
  @Before
  public void do_this_before_every_test() {
    // set up logic goes here
  }

  @After
  public void clean_up_after_a_test() {
    // teardown logic goes here
  }

  @Test
  public void testAddition() {
    assertEquals("Invalid addition", 2, 1+1);
  }

  @Test
  public void subtraction() {
    assertEquals("Invalid subtraction", 1, 1-1);
  }
}

This will result in a green-bar even though there is an error in the test of subtraction. Can you guess why?

It was that pesky little extends TestCase that I added from force of habit. The flips the switch to JUnit-3, which knows nothing about annotations, ignores my setup and teardown methods and looks for methods prefixed by test. It completely ignores the method named subtraction!

And of course, conversely, the following is just a plain old Java class that won't be acknowledged by either JUnit-3 or JUnit-4.

public class BusinessLogicTests {
  public void testAddition() {
    assertEquals("Invalid addition", 2, 1+1);
  }
}

Final word of advice, if you are using Struts Test Case, you have no choice but to write JUnit-3 style tests since the parent class of the framework (MockStrutsTestCase) extends Junit-3's TestCase.

Friday, January 09, 2009

Oh! Dash it...

I spent half a day cursing and spewing bile at JavaScript when after all was said and done, it was doing the right thing. The ignominy of it all...My writing here is part penance for casting aspersion at JavaScript and part hoping that you don't have to go through this agony.

I have a typical Java Server Page (JSP) with its share of HTML, scriptlets (gasp) and JavaScript. Much of the HTML is neatly organized in <div> tags. The JavaScript controlled the toggling the visibility of some of the divs.

When I tested the page and invoked the script, I didn't see the expected behavior. Firebug even told me that there was an error when the JavaScript was invoked. It went so far as to tell me that

document.getElementById(a_div_id) is null

By setting breakpoints in the script I quickly determined that, not surprisingly, the variable a_div_id referred to the id of a div on the page. It was there in the code -- clear as daylight. Here is about where the JavaScript cursing began. When that didn't help -- for the record, it never has -- I pulled out my trusty JS shell tool. Still muttering under my breath "I'll show you how it's done", I pounded the command

document.getElementById("line_items").style.display = 'block';

fully expecting to see the div with id=line_items to pop up.

Nope! Nothing but silence. The next port of call was the HTML source of the page in question. Sure enough, I was able to locate the div in question. However, the syntax highlighting showed that the entire div was commented out!!!! So, my script was correct in saying that there was no such element in the document.

Turns out that culprit was the use of textual em-dashes1 in my comments. Typically em-dashes are represented in text as two consecutive hyphens thusly

--

WYSIWYG editors (such as MS Word) are wont to convert that into a single em-dash



However, there is no such implicit conversion in a JSP or HTML page. So there it was smack in the middle of the comment.

<!-- This is bad news –– if you ask me. -->

It colluded with other such comments on the page to make a mess of what divs were actually there. I know HTML wonks out there are going to chime-in in unison with a "Well, D'uuuuh! Don't you know that that's improper HTML syntax". OK, for the rest of us, the Web Design group provides a pithy maxim to code by

An HTML comment begins with "<!--", ends with "-->" and does not contain "--" or ">" anywhere in the comment.

The formal definition of an HTML comment can be found at W3C.

My peeve now is that you would think that a self-respecting IDE like Eclipse2 would point that out for you. Now that I think back, even a puny little editor like VIM would flag this error. Heck, Eclipse doesn't even acknowledge <!> as a valid comment. If Eclipse had shown the code correctly as being in a comment, I would not have wasted time down the JavaScript rabbit hole in the first place.

All this is coming at the end of what has been a long week of futzing with Eclipse's sloppy handling of JSPs in general. Yes, this is with an Eclipse that is souped up to use the Eclipse Web Tools Project (WTP) "goodness".



1 em-dashes (—) Denotes a pause in thought, a parenthetical statement, or — more casually — an afterthought. For more on this subject, see What’s the Difference Between an Em Dash, En Dash & Hyphen?. BTW, did you know that an em-dash is called that because it is the width of a an 'm' in the current font? Useless trivia.
2 For the record, I am using the latest version 3.4.x (Ganymede).

Tweety thoughts

    follow me on Twitter