Dumbster Diving

(Tue Dec 28, 2004) [/TDD#

I'm currently writing an automated monitoring and notification program for a client. Users of their Java-powered web application can elect to receive email when certain things of interest have changed. The notification frequency is configurable. For example, as a user of this system you might want to be notified at the end of every day if something really important happened that day. For less important stuff you can wait for an email at the end of the week.

The monitoring step involves running a hunk o' Java when a timer pops to determine if something of interest has indeed changed since the last time interval. I'm using Quartz to schedule the monitoring jobs because it's light-weight, supports cron-like schedules, is as portable as Java can be, and it passes the fits-in-one-JAR-file test. Its only downside is that it's difficult to test schedules with long intervals. I'll come back to why I want to control its internal clock in a future blog. For now, trust that I know how many places Quartz directly calls System.currentTimeMillis() and it's greater than one.

The notification step uses JavaMail because my program lives inside of a bigger Java program and, thankfully, JavaMail is one of the less onerous Java APIs. Because successfully sending email is such an important part of the process, I'd like to test that it actually works. However, I'd like to do that without spamming my email account. I'm really only interested in validating that a correctly-formatted email was outbound. I could certainly mock it at the code level to make sure I'm using the API correctly. But wouldn't it be nice to have a fake SMTP server that squirrels away emails it receives and then lets you test for their existence?

That's where Dumbster comes in. It plugs in at the network level and intercepts email sent by JavaMail (or any other mail sender). Here's the test:

import com.dumbster.smtp.SimpleSmtpServer;
import com.dumbster.smtp.SmtpMessage;
import java.util.Iterator;
import javax.mail.MessagingException;
import junit.framework.TestCase;

public class MailSenderTest extends TestCase {
    
    public void testSend() throws MessagingException {
    
        String smtpHost = "localhost";
        int smtpPort = 2525;
        
        SimpleSmtpServer server = SimpleSmtpServer.start(smtpPort);
        
        MailSender sender = new MailSender(smtpHost, smtpPort);

        String toAddress = "barney@rubble.com";        
        String fromAddress = "fred@flintstone.com";
        String subject = "Caveman Greeting";
        String body = "Yabba Dabba Doo!";
          
        sender.send(toAddress, fromAddress, subject, body);

        server.stop();

        assertEquals(1, server.getReceievedEmailSize());
        Iterator inbox = server.getReceivedEmail();
        SmtpMessage email = (SmtpMessage)inbox.next();
        assertEquals(toAddress, email.getHeaderValue("To"));
        assertEquals(fromAddress, email.getHeaderValue("From"));
        assertEquals(subject, email.getHeaderValue("Subject"));
        assertEquals(body, email.getBody());    
    }
}

Notice that it starts the SimpleSmtpServer before sending the email and stops it after the email has been sent. Then the assertions verify that the server received the expected email. That's all there is to it: simple and unintrusive. Now I can deliver this test along with the code and anybody, anywhere can run it without unknowingly sending me email every time the test passes.

Practicing What I Preach

(Tue Dec 28, 2004) [/TDD#

Jeff Brekke astutely responded to my ConfigurationTest:

I'm wondering why you chose to use the constructor instead of setUp()? I guess I've grown a habit to always use setUp() and I was just wondering what the reasoning was. I try to question my habits often. ;)

Good thing you questioned it, Jeff... because I was wrong! Oh, there's nothing fundamentally wrong with using the constructor. The initialization code need run only once to capture the original system properties. But I put that code in the constructor for all the wrong reasons. Indeed, I fell prey to a cunning programmer trap. You see, I wanted to put the initialization code in the setUp() method. It makes for nice symmetry with the tearDown() method. When I'm reading tests and see those two methods, I know just what's going on.

But just as I was going for the setUp() method, I was struck by irrational fear: "Oh no, this initialization code could be slow. I don't want it to run for every test method. That will be really slow!" So, without measuring how slow, I took the bait. That's right, I was prematurely optimizing. And it bit me.

For some reason I forgot that a new instance of the test case is created for each test method. Consequently, the test case constructor is run for every invocation of the setUp() method. That's just how JUnit works. I was assuming the constructor was only run once, and that putting the seemingly slow code in the constructor would save time. I was wrong. Not only did it not save time, but it also compromised the readability of my test.

Here's the really bad (read: embarrassing) part: I've been aggressively preaching the perils of premature optimization. Indeed, my name is on the chapter in Bitter EJB that warns of performance tuning antipatterns. And the very first antipattern in that chapter is, you guessed it... Premature Optimization!

Shame on me. Perhaps by publicly falling on my sword I'll remember next time to focus on code clarity first, then measure for performance, if it matters. Thanks for keeping me on the rails, Jeff.

How Do I Test That? : Properties

(Tue Dec 28, 2004) [/TDD#

Today I'm crafting an app that's configurable through properties. (No, not XML, just plain 'ol properties. Silly me.) I want to write a test that, when it passes, gives me confidence that system-level properties (e.g. -Dkey=value) override properties specified programmatically. How do I test that?

Well, I could fire up the test from the command line, once with a system property set and once without, but that's messy. And my tests are control freaks. They don't like relying on external input.

The solution I settled on is fairly pedestrian. In fact, I'm betting you already have an answer in mind. But since I've written a similar test more than once over the last couple years, I figured it might be worth posting.

Keep in mind, there's no guarantee as to the order in which the test methods will be run. Thus, each test method wants its own "sandbox". So, not only are my tests control freaks, they're also sharing-challenged. Here goes:

public class ConfigurationTest extends TestCase {

    private Properties originalProperties;
    
    public ConfigurationTest() {
        originalProperties = new Properties();
        originalProperties.putAll(System.getProperties());
    }
    
    public void tearDown() {
        System.setProperties(originalProperties);
    }

    public void testSetProperty() {
        String key = "key";
        String value = "setValue";
        
        Properties properties = new Properties();
        properties.setProperty(key, value);
        
        Configuration config = new Configuration(properties);
        assertEquals(value, config.getProperty(key));
    }

    public void testSystemPropertyOverridesSetProperty() {
        String key = "key";
        
        Properties properties = new Properties();
        properties.setProperty(key, "setValue");
        
        System.setProperty(key, "systemValue");
        
        Configuration config = new Configuration(properties);
        assertEquals("systemValue", config.getProperty(key));
    }
}

That's a lot of code for a little bit of testing, you say? Well, it does appear to be a disproportionate amount of code, but I generally err on the verbose side and then wait until enough pressure builds to force a refactoring. I just can't see a good refactoring from here that enhances readability. Perhaps you will. In the meantime, I can continue running these tests at no cost.

I enjoyed writing this test (yet again) because it reminded me of the value of testability hooks. In this case, I can bypass the command-line interface completely and set system-level properties once the system is running. I'm learning that if I write my code driven by tests, then odds are those hooks emerge naturally. That is, my tests uncover new methods based on need rather than speculation. And sometimes those methods turn out to be quite handy outside the context of a text.

Stumped trying to write a test? Let me know. We'll both learn!