Sunday 4 February 2007

Complex expectations in EasyMock made easy using JSP EL

Setting up expectations in a mock framework like EasyMock is usually straightforward when dealing with simple types like integers or strings. But from time to time I run into situations when things become more complicated. Consider the following example.

We have a class Customer that has properties like name and address, but also a bunch of other things. We now want to test a method that populates the phone number field from some phone number directory. This test code could look something like this.


public void testPopulatePhoneNumber() throws Exception {
Services services = createMock(Services.class);
Customer loadedCustomer = new Customer();
loadedCustomer.setUserId("alb");
loadedCustomer.setFirstName("Al");
loadedCustomer.setLastName("Bundy");
// Set other properties
Customer saveCustomer = new Customer();
saveCustomer.setUserId("alb");
saveCustomer.setFirstName("Al");
saveCustomer.setLastName("Bundy");
saveCustomer.setPhoneNumber("555-SHOE");
// Set other properties
expect(services.loadCustomer("alb")).
andReturn(loadedCustomer);
expect(services.lookupPhoneNumber("Al", "Bundy")).
andReturn("555-SHOE");
services.saveCustomer(saveCustomer);
replay(services);

CustomerManager customerManager = new CustomerManager();
customerManager.populatePhoneNumber("alb");
verify(services);
}


(For simplicity I have a single interface for all sorts of good stuff, which of course wouldn't happen in a real system. Don't start building your own enterprise CRM system based on this. :-) )

The problem here is how you verify that the correct customer object is being passed into saveCustomer(). In this example EasyMock would use the equals() method of the Customer class to compare the expected object with the actual object. This means that to make all sorts of test cases work you would have to make the equals() method compare every single field in the class. Maybe this is not what you want. Maybe you want two customer objects to be considered equal if they have the same user id. But in this way the test cases have sort of hi-jacked the equals() method. Also you have to set up a lot of properties in the test cases and you get big equals() and hashCode() methods.

So I came up with the idea that you could use JSP EL (expression language) to write expressions that will make an assertion that the object is as expected. Writing an argument matcher for EasyMock that does this turned out to be pretty easy. Using this the expectation for saveCustomer() would become like this:


services.saveCustomer(assertBean(
"${bean.phoneNumber == '555-SHOE'}",
Customer.class));


Also you don't need the saveCustomer object any longer and the only properties that you have to set in the test case are those that actually has something to do with the test.

A small problem with this approoch though is that refactorings of the objects that are referenced within the EL expression will break the tests.

I have put up a small web page with the source code if you want to try it out at http://www.nilin.se/java/beanassert/.

Saturday 20 January 2007

Platform for Internet based desktop applications

Lately some people have been predicting that applications that are installed locally on a user's computer are on their way out and will be replaced by online applications. When someone wants to e.g. write a document he surfs off to his favourite word processing site instead of starting MS Word.

There are obviously benefits with this approach.

  • No need to install local copies and keep them updated and functional.

  • Easier to switch between vendors when you don't have to make an upfront investment in a sofware package. (Although I don't doubt that vendors will do everything they can to lock users in..)

  • Software piracy should become more difficult


Currently the best example is probably Google Docs & Spreadsheets. It's an impressive piece of Javascript. But is Javascript really the platform we want to use for implementing quality online software? These sorts of things were clearly not the intended use for the language.

Maybe it's time to bring back Java applets from the dead and brush it up a bit? If online applications are going to be a success I think we need something better than a scripting language on steroids.

Saturday 13 January 2007

Configuring a webapp

One thing I miss in J2EE webapps is a good way to configure the application outside the .war file. I want to be able to ship a .war file together with a properties file that the user can edit without having to unpack and re-pack the .war file. I also want to be able to deploy multiple instances of the same webapp in a container. Each having its own properties file.

There are a number of ways for a webapp to access properties.


  • A file under the web application root using ServletContext.getResource(AsStream)(). The problem with this is obviously that the file is inside the .war file.

  • Through the class path using Class.getResource(AsStream)(). Here you can access files outside the .war file. But there is a problem finding out what file to actually load in a particular webapp. At least if you use the init() method of a servlet. If you deploy several instances of the same webapp the thing that will be different is the context path that they are deployed under. And this information is only available in the HttpServletRequest object, not in the ServletContext.

  • Through system properties. This has the same problems as using the classpath and it may also not always be possible to set system properties parameters when starting the jvm.



I have installed a number of web application where I have had to find a property file deep inside the application server's deployment directories and edit it. Is there really no better way?