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

}

Thursday, February 18, 2010

Unit testing, better design

Ahem, so before we dig into some code, I would like to take a minute and say something.

I can be a very stubborn and obstinate person, and I am also incredibly lazy! As such, even having attended many lectures and throwing thousands and thousands of dollars at some of the finest academia, I ignored many of the simple, basic, fundamental principles of Computing they tried to instill in me.

Principles like
  1. Interface programming,
  2. Encapsulation,
  3. Separation of Concern [SoC],
I am positive there are more, but these three form a triad of sorts, themes in my most recent adventures.

This sad state of ignorance was further compounded by youth, inexperience, and entering the "real world", where constraints prohibited my ability to exercise these principles.

Many, many, many years later, after much trial, error, and tribulation I have entered something of a personal [and professional!] renaissance. Rediscovering some of these principles, I am continually finding new ways to simplify, extend, and robustify [alright, that is not really a word, but whatever] my work.

If that were not enough, then may I also add: Applying these simple yet powerful principles facilitates unit testing! Yes! That is right! Not only will you produce better work, but you will be able to quantitatively and qualitatively prove it is better.

If you are not yet sold, I do ask that you continue reading. Certainly, for the attentive reader, you will see these three principles come up time and time again in the [real world!] examples I present.

As I have said, it has taken me a long time to come around and see this for myself. It is my hope here and now, that you benefit from my experience: When designing a functional component, think about how Interfaces, Encapsulation, and SoC can help you. Invariably, such considerations will lead to better design and better product.

Tuesday, November 3, 2009

Unit testing, an introduction

Browsing stack overflow the other day, I noticed quite a few questions regarding unit testing. I responded to a few such questions, but found I repeated myself a bit. I then took a look around, and found a lot of resources about testing frameworks and evangelicals in favour of the practice, but precious few conferring advice or guidance in the subject of "how to unit test well".

This has [somewhat] inspired me to write a series of articles about lessons I have learned and experiences I have had in my years as a developer - and hopefully these will help :)

Thursday, October 22, 2009