Wednesday, April 7, 2010

Unit testing, how do I unit test WebService client X?

Another scenario I commonly encounter is the client side component to a WebService-based design.

For example,

// a client side view, displaying 
// server side profile data
public partial class ProfileView : UserControl
{
    private readonly Session _session = null;

    public ProfileViewModel ViewModel 
    {
        get { return (ProfileViewModel)(DataContext); }
        set { DataContext = value; } 
    }

    public ProfileView ()
    {
        InitializeComponent ();
    }

    // command implementation, fetches and assigns server side
    // data to client side data model
    public void Command_RefreshProfile ()
    {
        // embedded transmission codes! tsk tsk tsk
        // 
        // 1. get profile service
        ProfileWebService service = new ProfileWebService ();
        // 2. get profile
        Profile profile = service.GetProfile (_session);
        // 3. map to client side model
        ViewModel = new ProfileViewModel (profile);
    }
}

which is typical of a thick WPF client. Now, first thing to note is that the question
how do I unit test WebService client X?
is a bit of a misnomer. We are not actually interested in unit testing our WebService client, or in this case an instance of ProfileWebService. As with our WebService example what we really want to do is test our business logic and only our business logic.

Similarly then, the solution to this problem is to isolate our business from our remote invocation,

// a formal contract, defining all public operations 
// available for profile service
public class IProfileService
{
    Profile GetProfile (Session session);
}

public partial class ProfileView : UserControl
{
    private readonly Session _session = null;
    private readonly IProfileService _service = null;

    public ProfileViewModel ViewModel { get; set; }

    // we now pass in a reference to an implementation of our
    // profile service contract
    public ProfileView (IProfileService service)
    {
        InitializeComponent ();

        _service = service;
    }

    public void Command_RefreshProfile ()
    {
        // no embedded transmission codes! ah, nice clean code
        Profile profile = _service.GetProfile (_session);
        ViewModel = new ProfileViewModel (profile);
    }
}

When it comes to implementing IProfileService, we have one of two choices. If we are really lazy, and have access to the WebReference source,

// auto-generated web reference. except for these
// comments. and IProfileService definition below.
public partial class ProfileWebService : 
    System.Web.Services.Protocols.SoapHttpClientProtocol, 
    IProfileService
{
    // ...
}    

This has the advantage of no additional overhead or extraneous source files. The down-side is that maintenance is at a premium if the WebReference ever changes - which happens quite often in development. We must always remember to locate and modify the auto-generated classes with this interface definition!

Now, an alternative may be to automate the modification process, however that sounds like a lot of work to me. I would rather go the other route, and again, abstract the implementations a little.

Consider then,

// a thin wrapper for our auto-generated classes.
// has advantage of referencing most current web 
// reference when updated, without breaking existing 
// consumers
public class ProfileServiceProxy : IProfileService
{
    #region IProfileService Members

    // very simple pass through to actual auto-generated
    // implementation
    public Profile GetProfile (Session session)
    {
        // it is very important to keep this bit clean,
        // !!! NO BUSINESS LOGIC !!!
        ProfileWebService service = new ProfileWebService ();
        return service.GetProfile (session);
    }

    #endregion
}

Whichever route you go with, you have successfully isolated business from our remote service. This has the added advantage of being able to swap out ProfileServiceProxy with any implementation we like, say a WCF client, or a local instance of the actual service!

Our unit tests also benefit,

[TestMethod] 
public void Test_Command_RefreshProfile () 
{ 
    Session session = new Session ();

    IProfileService mockService = null;
    // instantiate mock with expectations 
    ProfileView view = new ProfileView (mockService); 
    view.Command_RefreshProfile (); 
    // verify results 
} 

Unit testing, how do I unit test WebService X with dependency Y?

Before moving on, I would like to build on our WebService example. In that example, we separated our business logic from our hosting solution. However, it may occur that our source solution has dependencies that must be fulfilled.

Consider this modified example,

// a profile web service, represents business logic 
// hosted from a web service
public class ProfileWebSevice : System.Web.Services.WebService
{

    // gets a profile for remote client
    [WebMethod]
    public Profile GetProfile (Session session)
    {
        Profile profile = null;

        // obtain authentication service from
        // web service context
        AuthenticationService auth = 
            (AuthenticationService)(Context.Cache["auth"]);

        // embedded business logic, bad bad bad
        // 
        // 1. authenticate
        if (auth.IsAuthenticated (session))
        {
            // 2. create sql connection
            // 3. get profile
            // 4. populate profile
            profile = new Profile (dataReader);
        }

        return profile;
    }

}

Note the dependency introduced by member variable Context. If we were to perform our simple refactor, we introduce compile-time issues,

public class ProfileService  
{ 
    public Profile GetProfile (Session session) 
    { 
        // COMPILE-ERROR: "Context" does not exist here! oh nos!!!
        AuthenticationService auth = 
            (AuthenticationService)(Context.Cache["auth"]);
        // ...
    } 
}

Fortunately, the solution is fairly straightforward. The key is realising our business logic of authenticate, fetch, instantiate is completely separate from the services that facilitate it. Our business class is a consumer of these services. From this perspective, it seems obvious to request these services as part of our invocation.

public class ProfileService  
{ 
    public Profile GetProfile (AuthenticationService auth, Session session) 
    {
        if (auth.IsAuthenticated (session)) { ... }
        // ...
    }
}

and our WebService now looks like

public class ProfileWebSevice : System.Web.Services.WebService
{
    [WebMethod]
    public Profile GetProfile (Session session)
    {
        AuthenticationService auth = 
            (AuthenticationService)(Context.Cache["auth"]);
        ProfileService service = new ProfileService ();
        Profile profile = service.GetProfile (auth, session);
        return profile;
    }
}

Again, our business logic does not care who or how a service is implemented, only that whomever is doing it, conforms to some known invocation. In this scenario, that an object instance of type AuthenticationService contains a method IsAuthenticated.

Our unit tests may look like

// test business logic without web service! yay! 
[TestMethod] 
public void Test_GetProfile_NullSession () 
{ 
    AuthenticationService auth = new AuthenticationService ();
    ProfileService service = new ProfileService (); 
    Profile actual = service.GetProfile (auth, null);
    // verify profile expectations
    // verify authentication service expectations
}

Thursday, April 1, 2010

I <3 unicorns ...

Happy April Fool's everyone! Loving the rainbow-y goodness of Stack Overflow's humour!