Friday, March 19, 2010

Unit testing, how do I unit test WebService X?

Most of this article appears in a StackOverflow post I submitted some time ago.

As the first bit of meat we cover in this series, we have a fairly typical scenario.
How do I unit test WebService X?
This scenario often presents itself as a WebService with embedded business logic within it.

For 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;

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

        return profile;
    }

}

I have worked on a number of web-based systems that employ this pattern, or something very similar. The four steps performed in sequence represent a very specific business flow, and it is not at all unreasonable to expect this flow to be tested.

Unfortunately, our example does not lend itself to testing very easily. For one, anyone reviewing this source would have difficulty separating our business from service hosting. While this distinction may seem trivial, it is often a source of great confusion. Do we need to host ProfileWebService? What about client-side proxies? Should we invoke from a web-client?

In short, the answer is no. WebProfileService and GetProfile represent parts of another tier altogether, that of web service hosting - and as far as testing is concerned, we are completely uninterested in testing [what is essentially] a third party hosting solution. Ultimately, we are interested in exercising our business logic and only our business logic.

What we really want is something like,

// a profile service without web service hosting,
public class ProfileService  
{ 

    // gets a profile
    public Profile GetProfile (Session session) 
    { 
        Profile profile = null;

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

        return profile;
    } 
}

which is completely free of any web service tom-foolery. Our web service then looks like,

// this web service is now a consumer of a business class, 
// no embedded logic, so does not require direct testing 
public class ProfileWebSevice : System.Web.Services.WebService
{

    [WebMethod]
    public Profile GetProfile (Session session)
    {
        ProfileService service = new ProfileService ();
        Profile profile = service.GetProfile (session);
        return profile;
    }

}

Finally, to test our re-imagined service,

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

Making your WebMethods simple passthroughs to proper underlying business classes and removing logic from them completely, allows you to target "real" user code as opposed to the plumbing of your typical WebService implementation.

Code fragment

Hello all, just testing new code formatting

public class SomeClass : ISomeInterface
{

    public SomeClass ( ) { }

    // interfaces

    #region ISomeInterface Members

    public void SomeWork ()
    {
    }

    #endregion

}