So for functional regression testing purposes, we need to be able to create tons of user accounts on our system. Every user account needs an email address. And of course, our system sends out notification emails to users so we need to test that stuff too.
Ok, so I want to write tests that can submit an email address to the application during user registration and then go read that user's mailbox for any notifications that have been received.
Here's the constraint: I can't instrument our application and stub out its email sending code with a stub. Remember, I'm writing functional regression tests here. Some of those will be smoke tests that we want to run in a live deployed environment. So the emails have to be real.
I don't want to use the corporate mail because of administrative overhead. I am certainly not interested in asking Ops to create thousands of test email accounts and ensuring their contents get purged on a regular basis. So we've got to have our own SMTP and POP3 server just for testing purposes.
And there are other requirements:
- Zero-administration. I can install the server on some machine and forget about it.
- Zero-configuration. I never have to worry about creating user mail boxes.
- Self-cleaning. It'll purge stale data automatically.
- XCOPY deployable. I can just drop it on a server and maybe run a script to install it as an NT Server and I'm done.
- Lightweight. It has such a negligible footprint that it's well below the radar in terms of resource utilization.
- Robust. It keeps going, and going, and going...
- Cheap. No per-user licensing, please!
Buy or Build?
A little Googling turned up nothing useful in this vein. (Which surprised me!) So we can't buy our way out of the problem. Darn.
Ok, build it. I'm sure we can cobble such a solution together using sendmail and a mess of perl scripts but I just don't have the time or inclination to dive into that kind of arcana. (Besides, they both make my eyes bleed.)
Can I do it in a couple hours with off-the-shell components? Yes! It's not free, but it's definitely cheap enough.
Introducing the Loopback Mail Server
Last night I spent a couple of hours and put together a simple SMTP / POP3 mail server as described above.
Here's what it does:
- The Loopback Mail Server stores mail messages in a folder called Mail within the application directory to contain the user mail boxes. Messages are kept around for a maximum of 7 days and are deleted automatically during periodic cleanup sweeps every 4 hours. (These parameters can all be configured if desired.)
- The SMTP server accept email sent by any user (no authentication is performed) to any recipient. Mail for the recipient is stored as flat files in an automatically created folder named based on the recipient's email address. The recipient's email address is only lightly validated to ensure that it is a valid file name.
- The POP3 server accepts connections from any user (no authentication is performed) and provides the ability to retrieve and delete mail messages. The POP3 user's name must be a valid email address which gets mapped back to the user's mail box folder on disk. If there is no such folder then the user's mail box is considered empty. Otherwise the files within the folder are enumerated and presented as the mail box contents for that user.
- The server can be run standalone by running the Standalone.bat file. In that case a cheesy little debugging dialog appears.
- More commonly, the server is installed or uninstalled as an NT Service by running the Install.bat or Uninstall.bat file.
The code archive includes a Readme.txt file to explain how the program is used. It also includes some rough sample code for sending/retrieving emails for testing purposes.
Using the Loopback Mail Server in a test
Here's what a typical test might do:
- Create a WatiN IE browser instance.
- Using WatiN, register a new user with a unique email address like firstname.lastname@example.org where the loopback.mydomain.com domain has appropriates CNAME and MX records to point at the host where the Loopback Mail Server is running.
- Using a POP3 client within the test (there's one included in the CleverComponents library), retrieve all emails for email@example.com.
- Verify that the user received a notification from the system regarding registration.
- Extract the registration verification link from the body of the email notification.
- Using WatiN, navigate back to the site and complete the registration process.
You'll notice we didn't need to tell the Loopback Mail Server anything about firstname.lastname@example.org. The system just sent an email to the account and it just worked. Likewise we won't need to do anything to delete the account when we're done with it. The Loopback Mail Server will take care of purging emails and mail folders 7 days after they are created (by default).
Experimenting with the Designer
Just for kicks since it was such a simple project I decided to see how much I could use the designer to build it in a RAD-friendly way (shh!). The experience was something of a mixed bag.
Overall I found the discoverability and responsiveness of the designer to be quite poor. I was often left scratching my head as to how to accomplish quite simple data binding tasks or as to why the components I needed weren't showing up in the toolbox.
Overall, using the designer was often more of a hindrance than a help. Even on the GUI layout of the debugging dialog, I encountered a lot of friction. I still can't imagine using it for anything but the simplest applications. *sigh*
Free and Open-Source Alternatives
Unfortunately I wasn't able to find any actively developed and maintained free open-source SMTP and POP3 server implementations for .Net. It's possible I just missed them. Moreover it didn't need to be .Net at all. It could have been Java, or C, or even Perl for all I care.
If you are interested in writing a mail server like this without any commercial dependencies or if you know of another one that is already available, please let me know! I'd love to use it and bundle it up as some kind of function regression testing package add-on for MbUnit or STAF.