Wednesday, September 23, 2009

Spring Bean Constructor Args

Spring bean configurations frequently contain beans to which you pass constructor arguments. When setting constructor args, you can either reference them by index or by type. This example shows referencing by index.

<bean id="MyBean" class="com.mycompany.MyClass">
<constructor-arg index="0" ref="SomeOtherBean">
<constructor-arg index="1" ref="SomeOtherBean2">
<bean>


In this example, the spring configuration will break if the order of the constructor arguments change. A more robust way to wire this is to set the constructors by type.

<bean id="MyBean" class="com.mycompany.MyClass">
<constructor-arg type="com.mycompany.MyClass1"
ref="SomeOtherBean">
<constructor-arg type="com.mycompany.MyClass2"
ref="SomeOtherBean2">
<bean>


Now if the order of the constructor arguments change, the bean configuration does not need to change.

It's not always possible to do this, particularly if you have multiple constructor arguments of the same type (although you can mix and match by index and by type), but I recommend referencing by type whenever possible.

Monday, September 14, 2009

Continuous Integration and Dependency Management

If you're not using continuous integration and dependency management systems, you should be. I use Maven with Hudson, and the results have been spectacular (although Hudson/Cobertura seems to have some issues with double reporting test coverage in maven2 projects, but I've been able to work around that).

My introduction to continuous integration was a few years ago, and I immediately saw the benefits of the continuous build. I was working on a multi-site team and didn't always know what everyone was doing at any given time. Having a continuous build gave me a high degree of confidence that the system was working reasonably well. I knew when tests broke immediately and it was generally clear where the problems were occurring so they could be fixed easily. Watching the build logs, I could see which parts of the system were most active and it was easy for me to focus on learning more about those parts. But, I think by now a lot of us have seen these benefits.

Dependency management, on the other hand, seems to be a newer technology. Although Maven and ant have been around for a while, in projects I've worked on, they weren't as widespread. If you aren't using a dependency management system, you're probably doing something like uploading the .classpath file from Eclipse into your repository, which now binds everyone to Eclipse, or people are responsible for manually setting the project in their IDE to link to all of the libraries in the lib folder and finding that something doesn't work when a new library gets added.

Enter the POM. In maven, the POM has a nice, clean declarative xml syntax for declaring a dependency.

<dependency>
<groupid>org.apache.maven</groupid>
<artifactid>maven-project</artifactid>
<version>${mavenVersion}</version>
</dependency>


And that's it, now you've pulled in the maven-project jars (maven supports transitive dependencies as well, so if X depends on Y, and you import X, it will import Y as well). Maven provides an impressive central repository (http://repo1.maven.org/maven2/) and there are numerous repository manager tools (Apache Archiva and Sonatype Nexus for examples). If you haven't realized by now, I'm excited by Maven and happy to rid myself of ant.

Tuesday, September 8, 2009

Automated UI Testing

I've often been asked how to automate testing for a UI. There isn't an easy solution to this problem, and automated UI testing is very difficult. An application's UI is frequently changing, sometimes just minor layout changes, and sometimes major reworks. Regardless, the change can be enough to throw off a lot of automated UI testing tools. So does that mean we shouldn't test the UI?

Every situation is different, but these are a few tips that will help make automated UI testing easier:

  1. Make the UI as thin as possible. Don't put business logic in UI components, even though it can be tempting (patterns such as Model-View-Controller help with this). An example:
    Difficult to test:

    JButton button = new JButton("Check if even");
    button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
    int num = Integer.parseInt(numberField.getText());
    if ( num % 2 == 0 ) {
    System.out.println("num " + num + " is even");
    }
    }
    });

    This is difficult to test because now the whole UI needs to be brought up just to test this function, and this is not conducive to automated testing. This can be refactored as follows:

    JButton button = new JButton("Check if even");
    button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
    int num = Integer.parseInt(numberField.getText());
    if ( NumberUtils.isEven(num)) {
    System.out.println("num " + num + " is even");
    }
    }
    });

    public static class NumberUtils {
    public static boolean isEven(int num) {
    return num %2 == 0;
    }
    }

    While this may not look like a big deal, the change is signficant. The NumberUtils class can have a simple unit test written for it and now that code is being tested outside the UI. It's easy to see that as functions become more complex, it becomes even more important to move them outside the UI logic.

  2. Wait until the last responsible moments to write UI tests. Don't write automated UI tests while the UI is still in a great state of flux. You'll spend more time maintaining them than you will find useful. This will vary from project to project, but I find this doesn't generally happen until later on in the project.

  3. Maintain automated UI tests like you maintain code. They should be run as part of a regression test (even if it's a nightly test as opposed to a continuous build since they may take longer to run). They generally can't be simple record/playback tests. While this is a good place to start, they really should be refactored by a developer to make sure that when the UI changes and tests break, minimal changes will be needed to the UI tests. For example, if multiple tests are looking for a particular table column, make that a static string somewhere (or even reference it from the code under test if you can and the string itself is not under test). This way if the string changes, you don't need to update the string in multiple places.
You may find that you don't have the time or resources to write automated UI tests. It's the hardest thing to test and usually gets cut from testing first. If you're not going to have automated tests for it (and even if you are), be sure that UI code only includes UI related functionality, not the algorithms being invoked by the UI.

Wednesday, September 2, 2009

Software People Will Use

I recently listened to a talk by Joel Spolsky (http://blog.businessofsoftware.org/2009/09/joel-spolskys-talk-at-business-of-software-2008-on-being-number-one.html) on making software that is number one in the industry and it inspired me to write a bit about it. He talks about 3 key points behind this, but the ones I want to focus on are making the user happy and obsessing about aesthetics. I think these are keys to any good piece of software, not just the industry leaders, although we should all strive to be there, especially if we are in the business of making money.

I'll start with obsessing over aesthetics. As developers (he discusses this in his talk, but most developers probably know this), we don't spend a lot of time thinking about aesthetics. We're concerned with making sure the software works, and if it works, that is what's important. True, this is important, but there's a lot more than just making it work. The first thing the end user sees are the aesthetics. If the software doesn't look nice, it will be off-putting. Remember, most users aren't programmers! The users are our customers, and we need to make a product that will appeal to them.

That leads me to the point of making the user happy. There are various ways to make the user happy. Spolsky talks about putting the user in control of the software. Make it easy to use, but let the user feel like they are controlling what happens. It shouldn't take a programmer to use it. I like to think software should be simple, yet sophisticated.

I recommend watching this talk, but if you don't, the key takeaway I took from it was that the software is all about the user. While we should already all know, this talk was a nice reinforcement.