Testing WCF Service Apps (Part 3 of 4)

Previous Posts:
Part 0 of 4: Introduction
Part 1 of 4: Testing the Service
Part 2 of 4: Testing the Client

Shout it

Testing Asynchronous Clients

Up to this point, we have tested the service and we have tested the client — both in isolation.  We have written unit tests and our code  has good coverage.  Unfortunately, my clients are not always synchronous.  In Silverlight client, for instance, the framework will not permit you to make synchronous service requests.  As it turns out, writing tests for asynchronous service clients is not straight-forward.  Thankfully, there are some hacks that you can take advantage of to write effective asynchronous tests.

Generating Asynchronous Service Clients

When you generate your service reference in Visual Studio, the advanced options allow you to specify an asynchronous proxy. 

Async

In a textbook example, your is a bit more complicated but it is still pretty easy to follow.  Instead of calling AllRecipes (as in the previous post), the class hooks the AllRecipesCompleted event and calls AllRecipesAsync().  When the service returns the results, the event is fired and the results are processed.

public class IngredientFinder
{
    public event EventHandler<IngredentFinderCompleteArgs> ProcessingComplete;
    private string _ingredientName;

    public void FindRecipes(string ingredientName)
    {
        var recipeService = new RecipeBoxServiceClient();

        _ingredientName = ingredientName;
        recipeService.AllRecipesCompleted += AllRecipes_Completed;
        recipeService.AllRecipesAsync();
    }

    void AllRecipes_Completed(object sender, AllRecipesCompletedEventArgs e)
    {
        var recipes = from recipe in e.Result
                      where recipe.ContainsIngredient(_ingredientName)
                      select recipe;

        if (ProcessingComplete != null)
            ProcessingComplete(this, new IngredentFinderCompleteArgs { Recipes = recipes });
    }
}

Making it Testable

Just like in part 2, the code works great but it is not at all testable.  IngredientFinder is tightly coupled to the RecipeBoxServiceClient.  In a unit testing environment, you cannot rely on WCF to host the service.  Unlike part 2, it is not as simple as replacing the concrete service with IRecipeBoxService.  The interface that is generated looks like this:

public interface IRecipeBoxService
{
    RecipeData[] AllRecipes();
    IAsyncResult BeginAllRecipes(AsyncCallback callback, object asyncState);
    RecipeData[] EndAllRecipes(IAsyncResult result);
}

I have no idea why Microsoft did this, but the interface does not include the interface that the concrete class implements.  Neither the AllRecipesCompleted event or the AllRecipesAsync method are found in the interface!  It includes the begin/end calls but those are some messy methods to use.  I don’t want to require my IngredientFinder class to be required to use messy methods just to make my code testable.  This is where my hack comes in.  It takes advantage of the fact that the RecipeBoxServiceClient is a partial class:

public interface IRecipeBoxServiceAsync : IRecipeBoxService
{
    void AllRecipesAsync();
    event System.EventHandler<AllRecipesCompletedEventArgs> AllRecipesCompleted;
}

public partial class RecipeBoxServiceClient : IRecipeBoxServiceAsync
{}

So what did I do here?  I created a new, asynchronous interface that includes the IRecipeBoxService interface and also includes the event and async method that is implemented in the concrete class.  After that, I tell the class to implement the asynchronous interface via the partial keyword.  I don’t have to write any implementation code because it has already been done for me — the interface simply didn’t include it.

Now that this is in place, we can modify the class slightly to pass the interface in via dependency injection:

public class IngredientFinder
{
    public event EventHandler<IngredentFinderCompleteArgs> ProcessingComplete;

    private readonly IRecipeBoxServiceAsync _recipeService;
    private string _ingredientName;

    public IngredientFinder(IRecipeBoxServiceAsync service)
    {
        if (service == null) throw new ArgumentNullException("service");

        _recipeService = service;
    }

    public void FindRecipes(string ingredientName)
    {
        if (string.IsNullOrEmpty(ingredientName)) throw new ArgumentNullException("ingredientName");

        _ingredientName = ingredientName;
        _recipeService.AllRecipesCompleted += AllRecipes_Completed;
        _recipeService.AllRecipesAsync();
    }

    void AllRecipes_Completed(object sender, AllRecipesCompletedEventArgs e)
    {
        var recipes = from recipe in e.Result
                      where recipe.ContainsIngredient(_ingredientName)
                      select recipe;

        if (ProcessingComplete != null)
            ProcessingComplete(this, new IngredentFinderCompleteArgs { Recipes = recipes });
    }
}

The Tests

The tests are also more complex.  The test needs to simulate an asynchronous service and fire events.  I am using Moq to mock out the service.

[TestFixture]
public class TestIngredientFinder
{
    private Mock<IRecipeBoxServiceAsync> _mockService;
    private IngredientFinder _finder;

    [SetUp]
    public void SetUp()
    {
        _mockService = new Mock<IRecipeBoxServiceAsync>(MockBehavior.Strict);
        _finder = new IngredientFinder(_mockService.Object);
    }

    private RecipeData Recipe(string recipeName, params string[] ingredientNames)
    {
        var result = new RecipeData {Title = recipeName};

        var quantities = new List<QuantityData>();
        foreach (string ingredientName in ingredientNames)
            quantities.Add(new QuantityData{Ingredient = new IngredientData{ Name = ingredientName}});

        result.Quantities = quantities.ToArray();
        return result;
    }

    private static AllRecipesCompletedEventArgs RecipeCompletedArgs(params RecipeData[] results)
    {
        return new AllRecipesCompletedEventArgs(new object[] {results}, null, false, null);
    }

    private static MockedEvent<AllRecipesCompletedEventArgs> RegisterCompletedHandler(Mock<IRecipeBoxServiceAsync> mockService)
    {
        var serviceCompletedHandler = mockService.CreateEventHandler<AllRecipesCompletedEventArgs>();
        mockService.Object.AllRecipesCompleted += serviceCompletedHandler;
        return serviceCompletedHandler;
    }

    [Test]
    public void Test_IngredientFinder_With_One_Recipe_That_Has_Cheese()
    {
        var serviceCompletedHandler = RegisterCompletedHandler(_mockService);
        _mockService.Expect(service => service.AllRecipesAsync());

        IEnumerable<RecipeData> recipes = null;
        _finder.ProcessingComplete += (sender, args) => recipes = args.Recipes;

        _finder.FindRecipes("Cheese");
        serviceCompletedHandler.Raise(RecipeCompletedArgs(Recipe("Mac&Cheese", "Cheese", "Macaroni")));

        Assert.That(recipes.Count(), Is.EqualTo(1));
        Assert.That(recipes.ToList()[0].Title, Is.EqualTo("Mac&Cheese"));
    }

    [Test]
    public void Test_IngredientFinder_With_Two_Recipes_That_Have_Cheese()
    {
        var serviceCompletedHandler = RegisterCompletedHandler(_mockService);
        _mockService.Expect(service => service.AllRecipesAsync());

        IEnumerable<RecipeData> recipes = null;
        _finder.ProcessingComplete += (sender, args) => recipes = args.Recipes;

        _finder.FindRecipes("Cheese");
        serviceCompletedHandler.Raise(RecipeCompletedArgs(
                                          Recipe("Mac&Cheese", "Macaroni", "Cheese"),
                                          Recipe("Grilled Cheese", "Cheese", "Bread")));

        Assert.That(recipes.Count(), Is.EqualTo(2));
        Assert.That(recipes.ToList()[0].Title, Is.EqualTo("Mac&Cheese"));
        Assert.That(recipes.ToList()[1].Title, Is.EqualTo("Grilled Cheese"));
    }

    [Test]
    public void Test_IngredientFinder_Finding_Nothing()
    {
        var serviceCompletedHandler = RegisterCompletedHandler(_mockService);
        _mockService.Expect(service => service.AllRecipesAsync());

        IEnumerable<RecipeData> recipes = null;
        _finder.ProcessingComplete += (sender, args) => recipes = args.Recipes;

        _finder.FindRecipes("chicken");
        serviceCompletedHandler.Raise(RecipeCompletedArgs(
                                          Recipe("Mac&Cheese", "Macaroni", "Cheese"),
                                          Recipe("Grilled Cheese", "Cheese", "Bread")));

        Assert.That(recipes.Count(), Is.EqualTo(0));
    }

    [Test, ExpectedException(typeof(ArgumentNullException))]
    public void Test_For_Null()
    {
        _finder.FindRecipes(null);
    }

    [Test, ExpectedException(typeof(ArgumentNullException))]
    public void Test_Constructor_With_Null_Service_Interface()
    {
        var junk = new IngredientFinder(null);
    }
}

Next Time

In the last post of this series, I will discuss functional testing.  How do you test the round-trip functionality of your application?  I will re-visit the synchronous client and show some tricks that allow you to test all points of your application without requiring the WCF infrastructure to be running. (part 4 of 4)

7 Responses to “Testing WCF Service Apps (Part 3 of 4)”

  1. Bill Campbell says:

    Hi,
    Sorry that I’m so late on commenting here but I just came across your posts. Thanks much – this is great!
    I was wondering – when you have your concrete class RecipeBoxServiceClient : IRecipeBoxServiceAsync that will have all the original Begin/End functions that came from the original interface (IRecipeBoxService) and so we will need to implement all of those original interface methods with not implemented or something, right?
    thanks,
    Bill44077

  2. admin says:

    Bill,

    No. That is not the case. It is a bit difficult to follow, but notice two things: First, I created IRecipeBoxServiceAsync to extend the automatically generated IRecipeBoxService interface. Then, I use the partial keyword to say that the concrete (also automatically generated) RecipeBoxServiceClient implements the Async interface. That is all you have to do… because the RecipeBoxServiceClient already implements the methods/events… but for whatever reason, the WCF code generator didn’t add them to the IRecipeBoxService… This is just a quick fix to add these async components to the interface so we can substitute it at test time.

    Does that clarify?
    Brian

  3. Bill Campbell says:

    Brian,
    That’s exactly what I thought you were saying but when I followed the same steps deriving my interface from the webservice interface and declaring the concrete classes as being partials I get build errors saying that my concretes don’t implement the methods in the original webservice interface. So I have:

    public interface IWebService // WebService generated Interface

    then:
    public interface IDataService : IWebService

    then:
    public partial RealDataService : IDataService
    or:
    public partial MockDataService : IDataService

    It looks like what I am missing is that you say that the ServiceClient class is also “generated” where I am creating my own concretes. I can’t seem to locate a ServiceClient class. Should this get generated when I update the ServiceRef? I’m thinking it should be created in the client side code somewhere. This is probably what I am missing.
    thanks!
    Bill44077

  4. Bill Campbell says:

    Hi Brian,
    Got it. I wasn’t thinking that the ServiceClient is actually in the Reference.cs file in the Service References. I guess the implications of this is that any time you update your Service References you would need to change the ServiceClient to implement your derived interface since it would get re-generated. I don’t think there is any way around that unless you know of some other trick. Does that sound right to you?
    thanks much!
    Bill44077

  5. admin says:

    Bill,

    Yes. That is exactly correct. I don’t know of any way to do it automatically. It would be nice if the WCF client proxy generator had a way to do it… but it doesn’t :(

  6. Bill Campbell says:

    Hi Brian,
    Would you by any chance have an example of using Rhinomocks for mocking out the Async calls? I’ve been searching for some time and most of the examples that I find are using the old Rhino syntax and I can’t figure out how to do this using the AAA syntax. This seems that it would be a very common pattern for Silverlight since everything is done async.
    Thanks so much!
    Bill44077

  7. Thank you Brian for the four part series, they are very helpful! :)

    Related to @Bill’s comments, I found a t4 template by Nikola Malovic that can automatically generate the WCF Async interface from the (sl)svcutil wcf client proxy class.

    http://blog.vuscode.com/malovicn/archive/2010/11/25/naked-mvvm-simplest-way-to-do-wcf-code.aspx

    It works very well! :D

Leave a Reply