Sometimes you want to mock an existing type of the base class library included in the .NET framework in your unit tests. There are type hierarchies which are well designed and have a certain level of abstraction. Maybe they directly inherit from a interface for example.
But sometimes this is not the case. Let’s take System.Net.Mail.SmtpClient as an example of a type which often needs to be mocked, because it needs a connection to a mail server to do its job correctly. In your unit tests, you want to make sure that in a certain case a mail will be send, but you don’t want to actually send or receive a mail by running the unit tests.
Example class using SmtpClient
An example class using SmtpClient might look like this:
public class Order { public bool UserWantsMailNotification { get; set; } public void Buy() { if (UserWantsMailNotification) { var message = new MailMessage(); message.To.Add("customer@provider.com"); message.Subject = "Your order on MyShop.com"; message.Body = "Your order has been placed."; var server = "mail.server.com"; var port = 25; var smtpClient = new SmtpClient(server, port); smtpClient.Send(message); } Console.WriteLine("Order placed."); } }
Dependency prevents us writing unit tests
The next step to consider is writing unit tests to test the behavior of our Buy method. We want to test if a mail is correctly sent, when a user wants a mail notification and it if it won’t be sent, if the user does not.
As you can see we have a direct dependency on SmtpClient on line 16 in our sample class above by instantiating a new object of the type SmtpClient. This means that if we would write a unit test right now, we wouldn’t have any chance to change the behavior of the method. In other words, we don’t have any possibilities to avoid sending a mail if we call the Buy method within our test method.
Resolving the dependency
If the SmtpClient class would realize an interface, we could simply accept an instance of an interface type by applying constructor injection. This time this is not the case. So we have to build something to make the Buy method testable.
We start by creating an interface which will allow us to inject the dependency later for production or test environment. This interface looks like this:
public interface ISmtpService { void Send(MailMessage message); }
As a next step, we write a class which will use the SmtpClient class. This is the class we will use in our applications to actually send the mails. This SmtpService could look like this:
public class SmtpService : ISmtpService { private readonly SmtpClient _smtpClient; public SmtpService(string serverAddress, int serverPort) { _smtpClient = new SmtpClient(serverAddress, serverPort); } public void Send(MailMessage message) { _smtpClient.Send(message); } }
Rewriting the Order class
It’s now time to use our newly written SmtpService in our sample code containing the Order class. Well, to be honest, we won’t directly use SmtpService, but we’ll depend upon its abstraction, the ISmtpService interface. Let’s just look at the code:
public class Order { public bool UserWantsMailNotification { get; set; } public void Buy(ISmtpService smtpService) { if (UserWantsMailNotification) { var message = new MailMessage(); message.To.Add("customer@provider.com"); message.Subject = "Your order on MyShop.com"; message.Body = "Your order has been placed."; smtpService.Send(message); } Console.WriteLine("Order placed."); } }
As you can see on line 5, I added a parameter smtpService of the type ISmtpService. Further, I removed the instantiation of the SmtpClient object from the method. On line 14 there is only the call to the Send method on the smtpService variable.
The calling code looks like this:
public void CallingMethod() { var order = new Order(); var server = "mail.server.com"; var port = 25; var smtpService = new SmtpService(server, port); order.Buy(smtpService); }
First, we create an instance of the Order class. After that we define the variables holding the mail server information needed for the SmtpService to work. Then we create an instance of the SmtpService class and provide the values declared before as constructor arguments. Finally we call the Buy method on the order object providing the smtpService object as an argument.
This way of injecting a dependency is called Parameter Injection. I’ve chosen this type of injection because in this case we only need the SmtpService for that single Buy method. You will more likely see Constructor Injection. It’s used if an entire class rather than a single method depends upon a specifc type.
Writing the unit tests
With the new implementation of the Order class, we are now ready to implement our unit tests to check if the behavior in the Buy method works as expected. I’ll first show the code, before I’ll explain what it does and how it works:
[TestClass] public class OrderTest { [TestMethod] public void Buy_sends_mail_if_user_wants_notifaction() { var order = new Order(); order.UserWantsMailNotification = true; var smtpService = A.Fake<ISmtpService>(); order.Buy(smtpService); A.CallTo(() => smtpService.Send(null)) .WithAnyArguments() .MustHaveHappened(); } [TestMethod] public void Buy_does_not_send_mail_if_user_wants_notifaction() { var order = new Order(); order.UserWantsMailNotification = false; var smtpService = A.Fake<ISmtpService>(); order.Buy(smtpService); A.CallTo(() => smtpService.Send(null)) .WithAnyArguments() .MustNotHaveHappened(); } }
In the first unit test we verify that a mail will be send, if the UserWantsMailNotification property is set to true. We do this by setting up the classes, faking a smtpService on line 9 and finally check if the Send method on the smtpService has been called.
For the mocking/faking purpose and the method call verification I used FakeItEasy in this case. I recommend FakeItEasy for beginners and experienced developers because of the simple API and the great functionalities provided. An introduction to faking/mocking and unit testing is not part of this article.
In the second unit test we verify that our code won’t send a mail if the UserWantsMailNotification property is set to false. We do this like we did in our first unit by faking the service and checking that the Send method was not actually called.
Of course you can clean up your unit tests that there is less or none copied code. I just let the code look like this that it is easy to understand which unit test calls which methods.
Conclusion
As you might have noticed reading this article, it is not always the case that every class from the base class library is testable by default. But you can work around so that your own library or application remains testable. This also applies if you use a third-party library which seems not to be testable by default.
Further reading
If you’re interested in learning some more really important things on unit testing, I would highly recommend reading The Art of Unit Testing by Roy Osherove. This book covers many import topics and helps you getting started. It is also worth reading for more experienced developers.