exceptionz

Thoughts on Technology, Methodology and Programming.

Archive for January, 2006

Using TypeMock.NET to mock an external dependency

Posted by Marcus Wyatt on 15 January 2006

In the scenario where your class consumes an external Web Service, testing your class is hard and difficult due to this external dependency on the Web Service. You could use a Service Stub, where you separate the dependency using an interface, but this forces you to create more code due to creating an interface and the service stub implementation. So I thought there must be a better, easier, less work way to mock this external dependency and here TypeMock.NET comes to the rescue. Well, let’s see some code:

I’ve created the following web service as an external dependency to consume in my class:

namespace DataLogic
{
  /// <summary>
  /// Simple WS to return a customer object to test
  /// how we will replace this external dependency
  /// </summary>
  public class DataService : WebService
  {
    [ WebMethod ]
    public DataResponse GetCustomer( DataRequest request )
    {
      DataResponse response = new DataResponse();
      response.Customer = new Customer();
      response.Customer.CustomerNumber = request.CustomerNumber;
      response.Customer.Firstname = "Maruis";
      response.Customer.Surname = "Marais";
      response.Customer.Title = "Mr.";
      return response;
    }
  }
}

I’ve created the following web service as an external dependency to consume in my class:

using System;
using System.Net;
using DataService;
 
namespace BusinessProcess
{
  public class Inquiry
  {
    // should be read from the config file of something
    private string url = @"http://localhost/DataService.asmx";
    public Customer GetCustomerDetailsDataInquiry( string customerNumber )
    {
      DataServiceRequest request = null;
      DataServiceResponse response = null;
      Customer customer = null;
      
      // Create the Request
      request = CreateDataInquiryRequest( customerNumber );
 
      // Call the Data Web Service
      response = GetCustomerInquiryDataResponse( request );
 
      // Validate the response returned
      // is the correct customer that is requested
      ValidateDataResponse( request, response );
 
      // Convert the data response to a
      // customer to return
      customer = FormatDataResponse( response );
    
      // Return the customer object
      return customer;
    }
 
    protected DataServiceResponse GetCustomerInquiryDataResponse( DataServiceRequest request )
    {
      // this is the dependency we want to replace
      DataService service = new DataService();
      service.Credentials = CredentialCache.DefaultCredentials;
      service.Url = url;
      
      // this is the specific call we want to replace
      DataServiceResponse response = service.GetCustomer( request );
      
      return response;
    }
 
    protected void ValidateDataResponse( DataServiceRequest request, DataServiceResponse response )
    {
    
      // here we want to confirm the requested customer is the correct
      // customer returned by the data web service, if it fails we want
      // to throw an exception
      if( request.CustomerNumber != response.Customer.CustomerNumber )
      {
        throw new Exception( "Incorrect customer returned" );
      }
    }
 
    // some more methods and code
  }
}

Note: The GetCustomerInquiryDataResponse method is declared as protected, without a virtual modifier. Most of the other Mocking frameworks would require you to modify the method to be public and virtual or you would need to extract an interface for the Inquiry class to be able to mock this method. TypeMock doesn’t need this to be able to mock the dependency; in actual fact TypeMock doesn’t mock the method. It actually mocks the DataService.

Here is the unit testing code where we used TypeMock.NET to replace the dependency on the Data Web Service. TypeMock ‘intercept’ the calls to Dataservice and mock these up:

using System;
using TypeMock;
using NUnit.Framework;
using BusinessProcess.ClassLib;
 
namespace BusinessProcess.Test
{
 
  [ TestFixture ]
  public class TestCustomerInquiry
  {
    // should be read from the config file of something
    private string url = @"http://localhost/DataService.asmx";
    
    [ Test ]
    public void GetCustomer_When_ValidCustomerNumberSupplied()
    {
      // 1. Initialize Type Mocks
      MockManager.Init();
 
      // Setup the Customer Details Inquiry class to test
      Inquiry inquiry = new Inquiry();
 
      // setup the expected object
      Customer expected = new Customer( "1234", "Grant", "Archibold", "Mr." );
 
      // 2. the DataService webservice is now being mocked
      Mock dataServiceMock = MockManager.Mock( typeof( DataService ) );
 
      // 3. setup our expectations
      dataServiceMock.ExpectSet( "Url" ).Args( url );
      dataServiceMock.ExpectAndReturn( "GetCustomer", GetResponse() );
 
      // GetCustomerDetailsDataInquiry should return a customer object we expect
      Customer actual = inquiry.GetCustomerDetailsDataInquiry( "1234" );
    
      // verify the objects are the same
      Assert.AreEqual( expected.CustomerNumber, actual.CustomerNumber, "Customer Numbers differ" );
      Assert.AreEqual( expected.FirstName, actual.FirstName, "FirstName differ" );
      Assert.AreEqual( expected.Surname, actual.Surname, "Surname differ" );
      Assert.AreEqual( expected.Title, actual.Title, "Title differ" );
 
      // 4. Verify that all calls were made
      MockManager.Verify();
    }
    
    #region Helper Methods
    
    private static DataServiceResponse GetResponse()
    {
      // Business Logic Response
      DataServiceResponse mockResponse = new DataServiceResponse();
      mockResponse.Customer = new DataService.Customer();
      mockResponse.Customer.CustomerNumber = "1234";
      mockResponse.Customer.Firstname = "Grant";
      mockResponse.Customer.Surname = "Archibold";
      mockResponse.Customer.Title = "Mr.";
      return mockResponse;
    }
    
    #endregion
  }
}

Let’s see what we did here:

  1. We initialize the TypeMock framework.
  2. Setup mocking of the DataService – all calls to data service will be intercepted.
  3. Here we tell TypeMock.NET what calls to expect, what parameters to validate and what results to return.
  4. Finally we would verify that all the expectations where fulfilled.

You might have noticed that I don’t pass any mocked object to the code that we are testing. This code is elegant and we are able to keep to the guidelines of writing good tests:

  • Test isolated code – Here we isolate our code completely by returning mocked values and by checking the ‘messages’ between the objects.
  • Tests should be fast – We don’t need to setup coupled code and this makes our test fast.
  • Tests have to be self contained – The entire scenario is contained in the test. (No need to connect to a corrupt database)
  • Tests have to be run by multiple users – As there are no shared resources this issue is fine.
  • Tests should be independent – This test can run alone.
  • Maintainable – As the test is simple, it is easier to maintain. Even if the data service changes its internal configuration this won’t effect the test
How does TypeMock.NET work?

TypeMock.NET uses an Aspect Oriented Programming Design, by creating a Mock aspect. Internally it uses the .NET Framework profiler API to monitor an application’s execution. When a method is loaded by the CLR, TypeMock.NET retrieves the IL and replaces it with instrumented IL code. TypeMock.NET does not change your original IL code, it simply inserts new code that calls the TypeMock.NET framework and returns mocked values

In the following test we test the ValidateDataResponse method throws the correct exception when we force an incorrect Customer to be returned (The mock returns a customer with customer number “1234” when the customer number we requested was “4567”):

  [ Test ]
  [ ExpectedException( typeof( Exception ), "Incorrect customer returned" ) ]
  public void ThrowException_When_CustomerNumberDifferFromRequestNumber()
  {
    // Data Logic Response
    DataServiceResponse mockResponse = new DataServiceResponse();
    mockResponse.Customer = new ClassLib.DataService.Customer();
    mockResponse.Customer.CustomerNumber = "1234";
    mockResponse.Customer.Firstname = "Grant";
    mockResponse.Customer.Surname = "Archibold";
    mockResponse.Customer.Title = "Mr.";
 
    // Setup the Object to test
    inquiry = new Inquiry();
    
    // the DataService webservice is now being mocked
    Mock dataServiceMock = MockManager.Mock( typeof( DataService ) );
 
    // set up our expectations
    dataServiceMock.ExpectSet( "Url" ).Args( url );
    dataServiceMock.ExpectAndReturn( "GetCustomer", mockResponse );
 
    // GetCustomerDetailsDataInquiry should return a customer object we expect
    Customer actual = inquiry.GetCustomerDetailsDataInquiry( "4567" );
 
    // if this line is reached, the exception is
    // not thrown, thus test fail
    Assert.Fail( "An exception should have occurred." );
  }

This type of testing would be impossible when doing integration or acceptance testing, thus you wouldn’t be able to test this exception behavior.

With TypeMock’s virtual mock ability it is now easy to mock any external dependency and test code in isolation without having to create stubs, interfaces or any other test friendly designs. Will this ability to easily mock these dependencies now cause us to write code that is more coupled due to the fact that we don’t have to think so much about designing our code with testing in mind? Maybe, it will. But I think that the ability that TypeMock.NET gives us, will help us to write better tests giving us better code coverage and the ability to produce well tested code faster. The only problem is that TypeMock.NET is a commercial product, which will hamper the adoption of the framework.

tags: , , , , , ,

Currently listening to: hiller-Ruhe – Winamp *** 48. DANNY TENAGLIA – 21 Schiller-Ruhe

Posted in Development, TDD | 6 Comments »