Kash Farooq's software development blog

.NET Developer

TDD with wrapper classes

Posted by Kash Farooq on January 12, 2009

Sometimes, during TDD, you come across a class that does not have an interface and therefore isn’t easy to test with Rhino Mocks. You’ll often find this is the case when you want to ensure that a class from the Base Class Library is being called correctly.
You can get around the problem using a wrapper class and an interface that describes the areas of the class you want to test.

For example, let’s say you have a method that uses System.Net.Mail.SmtpClient:

    public class SystemUnderTest {
        public void MethodUnderTest() {
            SmtpClient smtpClient=new SmtpClient();
            smtpClient.Port = 25;
            smtpClient.Host = "smtp.example.com";
            smtpClient.Send(new MailMessage{Subject = "Hello"});
        }
    }

SmtpClient is our external dependency. We want to test that the correct port and host has been set, and that Send is called with a MailMessage that has the subject “Hello”.

We start by creating an interface called ISmtpClient that includes the method and properties on SmtpClient that we want to test:

    public interface ISmtpClient {
        string Host { get; set; }
        int Port { get; set; }
        void Send(MailMessage mailMessage);
    }

We can now inject this into our system under test:

public class SystemUnderTest {      
   private readonly ISmtpClient smtpClient;

   public SystemUnderTest(ISmtpClient smtpClient) {
     this.smtpClient = smtpClient;
   }
        
   public void MethodUnderTest() {
      smtpClient.Port = 25;
      smtpClient.Host = "smtp.example.com";
      smtpClient.Send(new MailMessage{Subject = "Hello"});
   }
}

The test now becomes simple. Create a mock ISmtpClient, inject it into our system under test, and then assert that it was called correctly:

[Test]
public void MethodUnderTestSetsPortAndHostAndThenSendsMessage() {
	var mockRepository = new MockRepository();
	var smtpClient = mockRepository.DynamicMock<ISmtpClient>();

	mockRepository.ReplayAll();
	var systemUnderTest = new SystemUnderTest(smtpClient);
	systemUnderTest.MethodUnderTest();

	smtpClient.AssertWasCalled(x => x.Host = "smtp.example.com");
	smtpClient.AssertWasCalled(x => x.Port = 25);
	var mailMessage = (MailMessage)smtpClient.GetArgumentsForCallsMadeOn(x => x.Send(null))[0][0];
	Assert.AreEqual("Hello",mailMessage.Subject);
}

The final piece of the jigsaw is to wrap SmtpClient and implement ISmtpClient. The wrapper class will be used as the real implementation by the system under test:

public class SmtpClientWrapper : SmtpClient, ISmtpClient {}

Here is the final version of SystemUnderTest, using the wrapper class:

public class SystemUnderTest {
	private readonly ISmtpClient smtpClient;
	public SystemUnderTest() : this(new SmtpClientWrapper()) {}

	public SystemUnderTest(ISmtpClient smtpClient) {
		this.smtpClient = smtpClient;
	}

	public void MethodUnderTest() {
		smtpClient.Port = 25;
		smtpClient.Host = "smtp.example.com";
		smtpClient.Send(new MailMessage {Subject = "Hello"});
	}
}
Advertisements

Sorry, the comment form is closed at this time.

 
%d bloggers like this: