Friday, July 20, 2012

Hamcrest Matchers

Hamcrest matchers have been available for some time now, but I still don't see them being used regularly on projects. If you're not familiar with them, they are set of matchers that can be used with JUnit's assertThat syntax to make assertions easier to read and easier to diagnose when there are errors. Consider a test where we want to test if an object is a string. A traditional JUnit assertion might look like
Object o; // assume this is set during the test)
assertTrue(o instanceof String);
The default failure message is AssertionError. I could make the error message more meaningful by adding an error message (this still won't work if o is null).
assertTrue("expected String but was " + o.getClass(), o instanceof String);
Hamcrest matchers take of this for us. The assertion can be rewritten as
Now the error message is java.lang.AssertionError: Expected: an instance of java.lang.String got: <4%gt;. Note the is is just syntactic sugar and is optional. Hamcrest matchers also help to resolve the ambiguity between the expected and equals parameters. A typical assertion for comparing two strings might look like:
The assertEquals method is easy to get wrong and reverse the actual expected, which will produce backwards error messages. Using the hamcrest matchers, rewrite this is
assertThat(actual, equals(expected))
All hamcrest matchers follow this pattern of assertThat(actual, matcher), which make them read fluently. More information about hamcrest matchers can be found If you're writing Java unit tests, these provide a nice addition to the library.

Monday, July 2, 2012

Writing clean code

When I interview developers, one question I always ask is "What do you think makes code well written?" Almost without fail, I get the answer "It has a lot of comments." While I'm not saying that good code shouldn't have any comments, usually have a lot of comments is indicative of code that is hard to read. Take this simple example of a method that checks if a number if an even number less than 100 or an odd number greater than 200.
boolean isValidNumber(int number) {
   // number is valid if it is even and less than 100 or odd and greater than 200
   return (number %2 == 0 && number < 100 ) ||(number % 2 != 0 && number > 200)
Sure, with a little bit of thought I can understand what this does or by reading the comments (if I trust them, I'll get to that shortly). This version of the code
boolean isValidNumber(int number) {
  return (isEven(number) && number < 100) || (isOdd(number) && number > 200)
With just this simple change the code just reads better and without the documentation. While the documentation is valid in the first case and reading it does make the code just as easy to understand as the second version, what if I realized there was a change in the validation method and now my code looks like
boolean isValidNumber(int number) {
   // number is valid if it is even and less than 100 or odd and greater than 200
   return (number %2 == 0 && number < 100 ) ||(number % 2 != 0 && number > 201)
Now the documentation doesn't match the code. Six months later, I'm reading this code, and now I have a question - which is correct? Writing cleaner code makes the code easier to read and removes the potential for ambiguity. Code should be written to be read by people, the computer doesn't care what it looks like.

I started off by saying that I think good code can still have comments, but comments that are internal to methods should say why the code does what it does, not what it does. The code should be easy enough to read what it does. On a side note, I do think that documentation that says what the code does is appropriate for the javadocs public methods (or even on private methods since IDEs typically show the documentation when hovering over a method).

Talking about writing clean code is easy, doing it is much more difficult (which is why so few of us actually do). Practice and think about it consciously while writing code until it becomes second nature. I also recommend that all developers read Clean Code by Bob Martin ( at least once.