Sunday, February 15, 2009

Testing Webapps That Send Email

I've been working on a GWT-based web application ( that relies on email for some of its functionality. For example, it sends email when you register, and email when you ask for your password. Standard stuff.

I've been working towards having full-blown integration tests that hit the site to make sure everything works from end-to-end. There are really two tricky areas with this. First, we want the app to send the emails, and think it's sending them out, but without actually sending them to a real email address. Second, we do need to actually check those emails to make sure that the correct content went into them.

This weekend, it finally all came together, and the solution was very straightforward. It depends on two opensource components, and adds about 100 lines to code to glue it all together.

The first opensource component is Dumbster, a fake SMTP server that's controllable through java code. It listens on port 25, and acts like a normal SMTP server as far as the connecting app is concerned. The key difference is that it doesn't send out the emails, but collects them and makes them available via a Java API.

The second component is the well-known Jetty, a Java web server that is easily embedded into other applications, which is the key for us.

Here's how we pull it all together. When the test harness starts up, it completely initializes the database, readying it for the Selenium tests. It then starts Dumbster, and finally starts Jetty. The harness creates a handler that responds to a very limited set of HTTP requests:

/ - list all emails
/stop - calls System.exit(0)
/reset - restarts Dumpster, which throws away emails and restarts with an empty list
/<number> - returns the email at this index in the list.

Combined, these features allow Selenium tests to hit a URL and fetch a given email, validate the content of the email, and, in the case of my verification email, click on the link embedded in the email.

I thought you might want the code. It's not tested, no guarantees, yada yada, but it works for me. Grab it here: