To send email from Microsoft Exchange Server via code involves the use of SmtpClient objects . Since it interacts with an external exchange server, the unit tests on it is not so straightforward.
The proponents of TDD would like to use Dependency Injection(DI) technique to inject the SmtpClient dependencies into the mail sending class in order to facilitate unit testing. One would have to subclass the SmtpClient class in order to create a custom SmtpClient class that overrides the Send method and verifies that the method was properly used and inject it into your class. There are a lot of code to be written, just for the sake of testing the class and this could easily put the busy developers off.
Or, one can make use of the duck typing in C# 4.0 in order to swap the real SmtpClient object with a fake one for unit testing purpose. Again, one needs to create a custom class that looks like SmtpClient, sounds like SmtpClient , but is not a SmtpClient or a descendant of SmtpClient . Actually this method is improving over the previous one if and only if the object under test is sealed . And yes, you still have to write a lot of code.
A way that I found useful in doing unit testing, without inviting me to write more code, is to use Typemock's AAA syntax . Supposed that I have the following class
public class SendEmail
{
public string From
{
get;
set;
}
public List<string>To
{
get;
set;
}
private string ToStr
{
get
{
string str = "";
for (int i = 0; i<To.Count; i++ )
{
str += To[i];
if (i != To.Count-1)
str += ",";
}
return str;
}
}
public string Title
{
get;
set;
}
public string Body
{
get;
set;
}
public string Host
{
get;
set;
}
public void Send()
{
SmtpClient client = new SmtpClient();
client.Host = Host;
string addedTitle = "Automatic Regen:" + Title;
string addedBody = Body + "\nScanned";
client.Send(From, ToStr, addedTitle, addedBody);
}
}
All I have to do is to swap the SmtpClient class with a fake object and specify my expectations against it.
Here's the test code:
[Test, Isolated]
public void SendMailTest()
{
SendEmail sEmail = new SendEmail()
{
From="a",
To=new List< string > {"b", "bb", "bbb"},
Body="c",
Title="d",
Host="e"
};
SmtpClient clientFake = Isolate.Fake.Instance< SmtpClient> (Members.ReturnRecursiveFakes);
Isolate.WhenCalled(() => clientFake.Host = null).CallOriginal();
Isolate.Swap.NextInstance < SmtpClient > ().With(clientFake);
sEmail.Send();
Isolate.Verify.WasCalledWithExactArguments(() => clientFake.Host = "e");
Isolate.Verify.WasCalledWithExactArguments(() => clientFake.Send("a", "b,bb,bbb", "Automatic Regen:d", "c\nScanned"));
}
There is no need to modify the original code ( I don't have to supply a dependency of SmtpClient class), nor I have to construct an elaborate subclass of SmtpClient. All the setup needed is there in the same method as the test, so reading it would be easier.
This is how a mocking tool can help in unit testing.