I have a workflow activity that sends email (the code for this activity can be found here), and I wanted to write integration tests using SpecFlow. This creates an interesting problem. I don’t want to simply mock everything out, but I also don’t want to require a valid SMTP server and email addresses. I also want the test to pass or fail without having to check an email inbox.
Luckily, there are configuration options used by the SmtpClient class that can be used to create files when email messages are sent. This is accomplished by adding some simple code to your application configuration file. (Source here.)
<system.net> <mailSettings> <smtp deliveryMethod="SpecifiedPickupDirectory"> <specifiedPickupDirectory pickupDirectoryLocation="C:\TempMail" /> </smtp> </mailSettings> </system.net>
This solution is easy and it works, but it creates another problem: I want my test to run automatically on other machines. I don’t want to hardcode a path into the config file because I could run into problems with user permissions or directory structure. I found this blog post that demonstrates how to change the directory programmatically. The only thing I didn’t like about that solution is that it requires the app.config change shown above. I modified the posted solution slightly so that the configuration file section is not needed. Here’s the result:
var path = GetTempPath(); // get mail configuration var bindingFlags = BindingFlags.Static | BindingFlags.NonPublic; var propertyInfo = typeof(SmtpClient) .GetProperty("MailConfiguration", bindingFlags); var mailConfiguration = propertyInfo.GetValue(null, null); // update smtp delivery method bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic; propertyInfo = mailConfiguration.GetType() .GetProperty("Smtp", bindingFlags); var smtp = propertyInfo.GetValue(mailConfiguration, null); var fieldInfo = smtp.GetType() .GetField("deliveryMethod", bindingFlags); fieldInfo.SetValue(smtp, SmtpDeliveryMethod.SpecifiedPickupDirectory); // update pickup directory propertyInfo = smtp.GetType() .GetProperty("SpecifiedPickupDirectory", bindingFlags); var specifiedPickupDirectory = propertyInfo.GetValue(smtp, null); fieldInfo = specifiedPickupDirectory.GetType() .GetField("pickupDirectoryLocation", bindingFlags); fieldInfo.SetValue(specifiedPickupDirectory, path);
Using this code, I’m able to change the email delivery method and specify the output path programmatically. In my SpecFlow test, I create a temporary directory, process and verify email files created by my workflow, and cleanup. It works like a charm!