Pablo M. Cibraro (aka Cibrax)

My thoughts on Web Services and .NET development

News

Pablo Cibraro's VisualCV

Blogs

Personal

Programming

WCFMock, a mocking framework for WCF services

WCFMock, a mocking framework for WCF services. Not a very original name, but it was the first one that came out to my mind :). If you have been following my blog for a while, you might notice that I discussed different approaches in the past to unit test WCF services, here and here. One of the major pains that you will find today for unit testing WCF services is the static operation context (OperationContext and WebOperationContext). If you service implementation relies on that context for doing something, you will have a hard time trying to test that functionality.

For instance, it is very common in WCF REST services to use the context to set or get http status codes. With the current WCF bits, how can you do to unit test those services ?. The answer is WCFMock, a set of useful classes that will help you to remove all the explicit dependencies with the operation context, and still provide a good way to mock them from unit tests.

Let's see how WCFMock works in practice with a very simple example,

1. You have a WCF REST service that returns a RSS feed with a catalog of products

[ServiceContract]

public interface IProductCatalog

{

    [WebGet(UriTemplate = "?category={category}")]

    [OperationContract]

    Atom10FeedFormatter GetProducts(string category);

}

 

public Atom10FeedFormatter GetProducts(string category)

{

    var items = new List<SyndicationItem>();

    foreach(var product in repository.GetProducts(category))

    {

        items.Add(new SyndicationItem()

        {

            Id = String.Format(CultureInfo.InvariantCulture, "http://products/{0}", product.Id),

            Title = new TextSyndicationContent(product.Name),

            LastUpdatedTime = new DateTime(2008, 7, 1, 0, 0, 0, DateTimeKind.Utc),

            Authors =

            {

                new SyndicationPerson()

                {

                    Name = "cibrax"

                }

            },

            Content = new TextSyndicationContent(string.Format("Category Id {0} - Price {1}",

                product.Category, product.UnitPrice))

        });

    }

    var feed = new SyndicationFeed()

    {

        Id = "http://Products",

        Title = new TextSyndicationContent("Product catalog"),

        Items = items

    };

    WebOperationContext.Current.OutgoingResponse.ContentType = "application/atom+xml";

    return feed.GetAtom10Formatter();

}

 

 

This service implementation only relies on the WebOperationContext to set up the response content type, that is being done in the following line,

 

WebOperationContext.Current.OutgoingResponse.ContentType = "application/atom+xml";

 

2. You have now to find a way to get rid of that dependency so we can unit test that method. Here is where WCFMock comes to the rescue. The first thing you have to do is to define a new alias on top of your class,

using WebOperationContext = System.ServiceModel.Web.MockedWebOperationContext;

Optionally, you can wrap that sentence with a conditional compilation directive

#if DEBUG

using WebOperationContext = System.ServiceModel.Web.MockedWebOperationContext;

#endif

This is useful for instance, if you want to use the mocked version in development, and always the WCF version in production. That's all, you do not need to touch your existing service implementation at all, once you defined that alias, the service is ready to be tested.

3. For testing the service, I will use Moq, a pretty good mock framework created by my friend Cazzu.

[TestClass]

public class UnitTests

{

    [TestMethod]

    public void ShouldGetProductsFeed()

    {

        ProductCatalog catalog = new ProductCatalog(

            new InMemoryProductRepository(

                new List<Product>{

                new Product { Id = "1", Category = "foo", Name = "Foo1", UnitPrice = 1 },

                new Product { Id = "2", Category = "bar", Name = "bar2", UnitPrice = 2 }

            }));

        Mock<IWebOperationContext> mockContext = new Mock<IWebOperationContext> { DefaultValue = DefaultValue.Mock };

        IEnumerable<SyndicationItem> items;

        using (new MockedWebOperationContext(mockContext.Object))

        {

            var formatter = catalog.GetProducts("foo");

            items = formatter.Feed.Items;

        }

        mockContext.VerifySet(c => c.OutgoingResponse.ContentType, "application/atom+xml");

        Assert.AreEqual(1, items.Count());

        Assert.IsTrue(items.Any(i => i.Id == "http://products/1" && i.Title.Text == "Foo1"));

    }

}

Two pieces of code deserves some special attention, the code for creating the mocked WebOperationContext and the code required for verifying the expectations.

using (new MockedWebOperationContext(mockContext.Object))

{

    var formatter = catalog.GetProducts("foo");

    items = formatter.Feed.Items;

}

That will insert the mockContext object in the Thread Local Storage so it can be accessed later in the service implementation.  

The test also verifies that the ContentType header was set with the correct value in the operation,

mockContext.VerifySet(c => c.OutgoingResponse.ContentType, "application/atom+xml");

As you can see, all the magic is done by the MockedWebOperationContext (There is also a MockedOperationContext to replace the OperationContext). The implementation of this class is quite simple,

public class MockedWebOperationContext : IDisposable

{

    [ThreadStatic]

    private static IWebOperationContext currentContext;

    public MockedWebOperationContext(IWebOperationContext context)

    {

        currentContext = context;

    }

    public static IWebOperationContext Current

    {

        get

        {

            if (currentContext == null)

            {

                return new WebOperationContextWrapper(WebOperationContext.Current);

            }

            return currentContext;

        }

    }

    public void Dispose()

    {

        currentContext = null;

    }

}

The Current property of this class first tries to get an IWebOperationContext from the thread local storage (That could be set by the unit tests), and if none is available, it returns a wrapper to the original WCF context. As you can see, the overhead introduced by the class is minimal, it just a quick search in the TLS.

The project is now available to download from Codeplex at this location, http://wcfmock.codeplex.com/

Enjoy.

Posted: Mar 08 2009, 04:44 PM by cibrax | with 6 comment(s)
Filed under: ,

Comments

DotNetShoutout said:

Thank you for submitting this cool story - Trackback from DotNetShoutout

# March 8, 2009 5:49 PM

Lars Wilhelmsen said:

Hi Pablo,

Great work - I'll definitely check this out when I get back home from work.

cya,

 --larsw

# March 9, 2009 4:14 AM

cibrax said:

Cool. Let me know what think after playing a little bit with it.

Thanks

Pablo.

# March 10, 2009 8:58 AM

Sam Gentile's Blog (if (DeveloperTask == Communication && OS == Windows) said:

Windows Azure/Cloud Computing Cloud Computing Links March 10, 2009 Windows Azure and Cloud Computing Posts for 3/9/2009+ Agile/Mocks/Software Design/Software Development Tools Moq 3.0 RTM!!! Scott Ambler shares the the Manifesto for Software Craftsmanship

# March 10, 2009 4:14 PM

Vish said:

Very neat and very useful!

# March 12, 2009 5:07 PM

MIX09 Videos, Oslo, Cloud Memory and More « Tales from a Trading Desk said:

Pingback from  MIX09 Videos, Oslo, Cloud Memory and More &laquo; Tales from a Trading Desk

# March 24, 2009 5:37 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)