Saturday, 13 December 2008

Testing Exceptions In JUnit

As of JUnit 4, I'm sure everyone knows that testing exceptions was made a lot easier with the introduction of the @Test annotation that allows us to specify an exception that we expect to be thrown.

public static void throwNullPointer( String msg ) {
throw new NullPointerException( msg );
}

@Test( expected = NullPointerException.class )
public void nullPointerIsThrown() {
throwNullPointer( "Bad Call" );
}

This is much nicer than the old try{} catch{} mechanism that we used to have to implement each time we had to check for an exception.

There is, however, still a limitation to the declarative notation when checking for exceptions. There is no way to test the message. Maybe we don't need to test the messages from exceptions. We generally don't need to if the exception generated by a third party library, the type of the exception should be sufficient in this circumstance.

Another situation I encountered recently where it was necessary to use the try{} catch{} structure for testing exceptions was in an integration test to prove that certain unique constraints existed in a database.

@Test( expected = PersonAlreadyExistsException.class )
public void uniqueConstraintsTest() {
Person firstPerson = new Person( "fred" );
Person secondPerson = new Person( "fred" );
firstPerson.save();
secondPerson.save();
}

In this case it is possible for the Person.save() method to throw a PersonAlreadyExistsException. Looking at the test, we would expect secondPerson.save() to throw an exception. If, before we run this test, there already happens to be a person in the database called fred, which line is going to throw an exception? Not the line we expect. There is a problem with the setup of our test environment, but we don't know it because the test passes anyway, it got the exception it was expecting.

To make sure the exception is thrown exactly where we expect requires us to fall back to the old method of try{} catch{}:

@Test
public void rigorousUniqueConstraintsTest() {
Person firstPerson = new Person( "fred" );
Person secondPerson = new Person( "fred" );
firstPerson.save();
try {
secondPerson.save();
fail( "Expected PersonAlreadyExistsException" );
} catch( PersonAlreadyExistsException ex ) {
assertEquals( "Person with name [fred] already exists", ex.getMessage() );
}
}

Having spent some time working with Groovy I've seen a much nicer replacement for the try{} catch{} structure:

@Test
public void betterRigorousUniqueConstraintTest() {
Person firstPerson = new Person( "fred" );
Person secondPerson = new Person( "fred" );
firstPerson.save();
String message = shouldFail( PersonAlreadyExistsException ) {
secondPerson.save();
}
assertEquals( "Person with name [fred] already exists", message )
}

Here the shouldFail method allows us to specify that the code in the closure should throw a PersonAlreadyExistsException, and we can optionally check the message afterwards if we wish. It would be extremely useful to have this option available when testing in Java, even if the syntax is a bit uglier.

@Test
public void betterRigorousUniqueConstraintTest() {
Person firstPerson = new Person( "fred" );
final Person secondPerson = new Person( "fred" );
firstPerson.save();
String message = shouldFail( PersonAlreadyExistsException.class, new TestCode() {
public void run() {
secondPerson.save();
}
});
assertEquals("Person with name [fred] already exists", message);
}

The implementation for this is fairly simple and once it is done we need write no more try{} catch{} blocks in our test code at the risk of forgetting to put a fail call in correct place.


public class ShouldFail {

private Class expectedException;

public ShouldFail( Class expectedException ) {
this.expectedException = expectedException;
}

protected String test( TestCode testCode ) {
try {
testCode.run();
fail( "Expected " + expectedException.getName() + " but no exception was thrown.");
return null;
} catch( Throwable ex ) {
if( ex.getClass().equals( expectedException ) ) {
return ex.getMessage();
} else {
fail( "Expected " + expectedException.getName() + " but got " + ex.getClass().getName() );
}
}
return null;
}

public static String shouldFail( Class t, TestCode code ) {
return new ShouldFail( t ).test( code );
}
}

In conclusion, I like, and do use the expected property of the @Test annotation in most cases. The use of a shouldFail closure only gives me value when I want to:

  • test the message that comes back on the exception, or
  • there is a chance that the exception we expect can be thrown more than once in the test.

In these cases, I would much rather use a template than implement the exception checking logic each time.

Friday, 12 December 2008

Agile Manifesto: It's the principles that bind us

Anyone who has taken even the slightest interest in Agile probably knows that it is based on a manifesto that has four core values:

"Individuals and interactions over processes and tools"

"Working software over comprehensive documentation"

"Customer collaboration over contract negotiation"

"Responding to change over following a plan"

Followed by the caveat:

"That is, while there is value in the items on the right, we value the items on the left more."

The great thing about the manifesto is that it is short and sweet. Even I can remember it. It's a simple set of values that people can keep in the back of their mind when making every day decisions. The problem with these values is that they are very loose and open to misinterpretation. Looking at the values for the first time, one might be tempted to start investigating agile software development further, but how to begin? That's the question.

Perhaps you should buy a book on agile? There are plenty available. A common approach, I know I was guilty of this once, is to get your hands on a book like Extreme Programming Explained by Kent Beck, read it from front to back, pick out the things that you think are interesting and start banging on about this being agile development. Maybe it is, maybe it isn't, that really depends on how well you understood the book and how open minded you are after reading it.

Maybe you know someone in another company who is "doing agile" that can help? You could seek out an agile consultancy to get you started. You could, he says holding his head in his hands, just stop documenting things, throw out any plans for the next few months, get a few people together and start cranking out code. See how far you get.

Let me make a suggestion. After reading the manifesto, click through to the following page titled Principles behind the Agile Manifesto. Read the twelve principles there. Consider them. Chew them over with some friends and colleagues. Savour them like a fine wine. These are the guiding principles that help give context to everything else that you might read or hear about agile.

If you don't agree with these principles, if you can find no common ground with the way you want to work and these principles, then stop. Leave agile software development well alone. Find some other way, and whatever you do, don't call it agile! Oh, and if you do find a better way, please let me know.

Agile software development is a nebulous thing. Mainly because it is a philosophy rather than any methodology. Yes there are methodologies that adhere to the agile philosophy, but it is entirely possible for a team to be using one of these methodologies and not be adhering to the agile principles.

Let's consider a Scrum team that doesn't write automated unit tests. Can they be agile? They are following the Scrum practices of regular product increments, using burn down charts, performing a demo and reflecting at the end of each sprint. They are using all the tools provided by Scrum to help self organising teams find the best way they can of working together. So, given this and the definition of agile taken from the values of the manifesto, who can say this team is not doing agile software development?

Well, after considering the principles behind agile software development, I'm willing to go so far as to say that there is a very good chance that they are not very agile. Here is my line of reasoning. One of the principles of agile states:

"Continuous attention to technical excellence and good design enhances agility."

I don't know how, or am not smart enough, to design and code everything correctly first time. I have to rely on refactoring to improve my code, and this is where I need automated unit tests. I can't reliably refactor my code without good unit test coverage and so I can't follow the principle of technical excellence and good design without them.

The reason I hedged my bets in the statement about the agility of this particular team is because I can't know everything about their approach, and they may have a very good way of ensuring good design and technical excellence without refactoring and unit tests. I doubt it, but I remain open minded.

If we can agree that agile software development is a philosophy, rather than a methodology, with a set of guiding principles then I believe we have a much stronger foundation for handling the increasing interest in agile adoption. These shared principles give more substance to the agile manifesto and can help identify situations where teams following agile methodologies end up failing where teams adhering to agile principles would have succeeded.


NB. Please don't consider my use of Scrum in the example above as an attack on the Scrum methodology. In the example above it is the collective team that I consider to be failing to adhere to the agile philosophy, rather than Scrum not being a suitable vehicle for agile.