(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.
(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.
(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!