Many of the WCF services that we build today rely on the WCF context (OperationContext and WebOperationContext) for performing different things, specially REST services where the context is necessary for settings and getting Http status codes or headers. The fact that the WCF context does not expose interfaces or base classes complicates the unit testing a lot.
In order to test a WCF service with what we have today, we have to either test the service as a black box (integration tests, which requires a lot of plumbing code to setup all the WCF infrastructure for the test, channels, host, client, etc) or create some wrappers to encapsulate the WCF context behavior.

WCFMock is a solution for unit testing WCF services, it provides 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"); 

Last edited Mar 8, 2009 at 8:13 PM by pcibraro, version 2