I frequently get this question about unit or acceptance testing: If sending e-mail or some other sort of notifications is required by the business process, do we test this and how? This arrived again yesterday from a reader, in the following form:

…and one component of our application is email notifications (pretty straightforward stuff). This occurs on registration, forgot password, etc. I am having a tricky time thing about how to test to make sure these email notifications actually arrive. Is it common practice to use the same tool to login to a dummy email account on gmail to make sure the message makes it there?

This seems like a hack, and I’m curious if you’ve ever had to deal with a similiar situation.

First of all, I regard sending e-mail synchronously from the web request as a very bad practice. Talking to an external system can slow down your business processing (and unit testing) and makes the whole process more error-prone as the DNS or MX transport to the external system might time-out. This might cause problems even when the e-mail server is local, since some e-mail servers try to verify the recipient address or server before confirming that the message is accepted. This is especially important if you are working in a transactional context eg you should only send an e-mail if the registration transaction is successful, but you don’t really know that until the whole thing commits. If the database commit fails, you can’t really revoke the message. If you send the message after committing, then there is a chance that the system will die in between and you’ll commit without sending a message.

What I tend to do is use a queue (MSMQ/ActiveMQ/JMS or whatever is available for your platform) and just enqueue the correct message there during the business process. You can then use a mock queue for unit testing and verify that the correct message is queued.

On the other side of the queue, you can have one or more queue processors that pick up the messages and ship them off to the mail server, resend in case of failure etc. That remains a tricky part to test, but you can write an integration test that posts a message to the queue, wait a minute and then check the e-mail mailbox with an automated script. This can run overnight, so you don’t have to run it all the time.

This way you split the functionality into something that is likely to change and should be tested frequently (business process and mail content) and something that is unlikely to change at all after you first write it and can be regression-tested overnight (SMTP message delivery). The first part is nice and quick to test because you can run everything on a developer box with a mock queue and check it from a unit testing tool.