Search This Blog

Loading...

Friday, December 5, 2008

Unit Testing SmtpClient class with TypeMock AAA Syntax

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.

1 comment:

Scott Bellware said...

I wouldn't think of sub-classing the smtp class. That conclusion comes from an unrecognized bias on your part.

The code that you have here works, and at least it's tested, but that's only part of the way to the level of productivity you could have by surfacing more meaningful abstractions, and using composition rather than inheritance as your default OO design guideline.

There is an unbelievably large well of productivity that is tapped when design patterns are learned, practiced, and used. But first you have to understand which kinds of designs merely work and which kinds of design work AND enable greater productivity - and hopefully why.

Even without understanding the "why", you can still benefit of the lasting personal productivity that comes from understanding OO design fundamentals. And by understanding a few key principals and by following a guidepost process like TDD you can get this productivity almost immediately while learning why it works.

There is a much more productive design pattern and principle waiting to be tapped in your code here, but sub-classing .NET framework classes is not it.

You danced around it in mentioning dependency injection. The guiding principle is dependency inversion. Both are used together to achieve designs that can be arrived at by learning how to use TDD to surface design flaws before they are created.

Even though you've solved the testing problem in your code with TypeMock, you haven't taken advantage of the rest of the OO tools at your disposal that will make you even more effective.

If you've adopted unit testing, and then learned TypeMock, then your obviously on a forward-directed path to improvement. Your next level of improvement is in using design as your productivity enabler rather than tools.

No matter how good your tools are, they are always something like an order of magnitude less effective than design in generating and sustaining productivity.