Subscribe Now

ABC, 123, Ruby, C#, SAS, SQL, TDD, VB.NET, XYZ

Friday, November 16, 2007

The Other Mother of Invention: Impatience

On 11/8, I blogged about Unit Testing an Email Method. Things have happened since then...



Bad Sleep

You may have heard the saying about necessity or laziness being the mother of invention, but I say impatience is a good mother, too. I say this based on the fact that I quickly got sick of waiting for my email unit tests to run when I was running the whole batch of unit tests in NUnit. Pretty soon I had them [Ignore]'d out. I was distressed about the yellow instead of the green, but my impatience was stronger. One property of a good unit test is that it runs rapidly.



Eventually (doh!) it dawned on me that I could both have and eat my cake. How? I modified my email unit test to query my Outlook Inbox every two seconds to see if the target email had arrived and exit early if so (with success). Otherwise, it would sleep for two more seconds and check the inbox again -- up to a maximum of eight repetitions (i.e., 16 seconds).



This refactoring made me quite the happy camper. Imagine my joy as I ran all of my unit tests quickly without the manditory 10-second waits for the tests involving email. I, like many NUnit users I am sure, just love to see that green bar -- and as soon as possible!



I took all of the Outlook-related goo and put it in a separate class called OutlookInbox with a single public (static) method called EmailReceived. I just pass in the subject line of the email I am expecting to receive and the method works its magic and I get back a boolean value telling me if the email arrived in time or not. The responsibility for doing the 2-second chunks of waiting is delegated to this method and the work of actually interfacing with Outlook is delegated to a helper method called checkInbox.



using System;
using System.Runtime.InteropServices;
using Outlook = Microsoft.Office.Interop.Outlook;

namespace TestHarness
{
public class OutlookInbox
{
private OutlookInbox() { }

public static bool EmailReceived(string subjectline)
{
const int MAXTRIES = 8;
int tries = 0;
bool emailReceived = false;
do
{
tries++;
System.Threading.Thread.Sleep(2000);
emailReceived = checkInbox(subjectline);
} while (emailReceived == false && tries < MAXTRIES);
return emailReceived;
}

private static bool checkInbox(string subjectline)
{
//This webpage (http://www.developerfusion.co.uk/show/4667/) was helpful in
//developing this code.
Outlook.ApplicationClass outlookApp = null;
Outlook.NameSpace outlookNS = null;
bool emailArrived = false;
try
{
outlookApp = new Outlook.ApplicationClass();
outlookNS = outlookApp.GetNamespace("MAPI");
outlookNS.Session.Logon("outlook", "", false, true);
Outlook.MAPIFolder inbox = outlookNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
foreach (Outlook.MailItem email in inbox.Items)
{
if (email.Subject == subjectline)
{
emailArrived = true;
email.Delete();
}
}
}
finally
{
if (outlookNS != null)
outlookNS.Logoff();
}
return emailArrived;
}
}
}


Now my unit test code looks like this. This is testing a web service that sends email. The business about ServiceConnections.WebServiceBaseUrl() manages whether we are pointing at the dev or prod service.



using System;
using NUnit.Framework;
using IndependentContractorApp.BusinessLayer;

namespace TestHarness
{
[TestFixture]
public class EmailServiceTestSuite
{
[Test]
public void SendRegularText()
{
string asmx = "EmailService.asmx";
string url = ServiceConnections.WebServiceBaseUrl() + asmx;
EmailWebService.EmailService svc = new EmailWebService.EmailService();
svc.Url = url;
svc.Credentials = System.Net.CredentialCache.DefaultCredentials;
DateTime now = DateTime.Now;
svc.SendRegularText("me@mycompany.org", "SendRegularText Test Email - " + now, "Body Text");
Assert.IsTrue(OutlookInbox.EmailReceived("SendRegularText Test Email - " + now),
"Email failed to arrive");
}
}
}


NUnit green bar of success

Ahh, a thing of beauty. :)


No comments: