Wednesday, October 30, 2013

Test Driven Development - Part V - Dealing External Dependency with Mock

As I mentioned in the previous session, Stubs are used to test the logic of the class under test  without considering how the dependant objects interact with thc class under test. Some times we need to test how the class under test interacts with the dependant objects or we need to make sure that the class under test interacts correctly with the dependant objects. Mocks are being used in this scenario. So by using Mocks, we are testing wheather the class under test has interacted correctly with the dependant object by verifying the Mock object.


Consider a scenario like the FileUpload class is logging the errors  and whenever an exception happens to the logging, it will mail the exception to the given mail address. Here we are using two dependent classes. One is for logging the errors and one for mailing the exception. If you want to test whether the mails are sending correctly for the exceptions we have to check the mail service object. For that we have to use the  Mock. A stub can be applied for Log service object. The below code shows how to implement it.

1. LogService Interface

 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
 namespace ManualMock_TDD  
 {  
   public interface ILogService  
   {  
     void WriteLog(string logmessage);  
   }  
 }  

2. MailService Interface

 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
 namespace ManualMock_TDD  
 {  
   public interface IEmailService  
   {  
     void SendEmail(string To, string Subject, string Body);  
   }  
 }  

3. The FileUpload Class

 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
 using System.IO;  
 using ManualMock_TDD;  
 namespace Simple_TDD  
 {  
   public class FileUpload  
   {  
     private ILogService _LogService;  
     private IEmailService _Email;  
     public ILogService LogService  
     {  
       get { return _LogService; }  
       set { _LogService = value; }  
     }  
     public IEmailService EmailService {  
       get {return _Email ;}  
       set {_Email=value ;}  
     }  
     public void ValidateFilename(string filename)   
     {  
       //return ValidateObj.IsValid(filename);  
       if (filename.Length < 8)  
       {  
         try  
         {  
           LogService.WriteLog("file name is too short");  
         }  
         catch (Exception ex)  
         {  
           EmailService.SendEmail("mail@me.com", "Error occured while writing log", ex.Message);   
         }  
       }  
     }  
   }  
 }  

4. The Unit Test Class

 using Simple_TDD;  
 using Microsoft.VisualStudio.TestTools.UnitTesting;  
 using System;  
 namespace ManualMock_TDD.Tests  
 {  
   [TestClass()]  
   public class FileUploadTest  
   {  
     [TestMethod]  
     public void ValidateFilename_EmptyFileName_WillSendEmail()  
     {  
       StubLogService logservice = new StubLogService();  
       logservice.ToThrow = new Exception("filename to short");  
       MockEmailService eMailService = new MockEmailService();  
       FileUpload Target = new FileUpload();  
       Target.LogService = logservice;  
       Target.EmailService = eMailService;  
       Target.ValidateFilename("abc.txt");  
       Assert.AreEqual("mail@me.com", eMailService.To);  
       Assert.AreEqual("Error occured while writing log", eMailService.Subject);  
       Assert.AreEqual("filename too short", eMailService.Body);  
     }  
   }  
   class StubLogService : ILogService  
   {  
     public Exception ToThrow;  
     public void WriteLog(string msg)  
     {  
       throw ToThrow;  
     }  
   }  
   class MockEmailService : IEmailService  
   {  
     public string To;  
     public string Subject;  
     public string Body;  
     public void SendEmail(string to,string subject,string body)  
     {  
       To = to;  
       Subject = subject;  
       Body = body;  
     }  
   }  
 }  


In the example, the FileUpload class is using two interface objects. One for log service and another for email service.  For unit testing we use stub for  Log Service and mock object for Email. We need mock object for email here because we are going to check the content of the mail sent by the FileUpload class. To generate the scenario, the StubLogService will throw an exception. Then the FileUpload class will  call the mail service and will send the exception to the address given. The assertions will be done on the mail object. The last three lines in the test method is verifying the mail object to ensure about the exception handled in the  FileUpload class

No comments:

Post a Comment