Brian Genisio's House of Bilz

  Home  |   Contact  |   Syndication    |   Login
  60 Posts | 0 Stories | 111 Comments | 0 Trackbacks

News

Locations of visitors to this page

Archives

Post Categories

Who am I?

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

Shout it

Functional Testing the WCF Application

In functional testing, the goal is to test as much of the application that you can to determine that it does what you want from a functional perspective.  It differs greatly from unit testing in that a unit test is only concerned with an individual class.  Functional tests are concerned with testing the interactions of the objects in the system from the user input to the user output.

I thought it would be easiest to draw out what the data mining application is doing in terms of data flow.  This particular application is a data mining application which queries a service to pull out data that the user wants.  In this case, the question that the user wants answered is "What recipes exist in the database that include a given ingredient?"  The data flow goes like this:

App.Diagram

The user types an ingredient into the console.  The console launches our application and calls "Main" with the arguments that include the ingredient in question.  The "Main" routine creates an IngredientFinder object which requests a list of all recipes known to the service.  It does this by asking the service proxy which uses WCF to ask the actual RecipeService, which may exist anywhere on the planet.  The actual RecipeService asks the Business Objects which in turn queries the database for all known recipes.  The database returns the results to the Business Objects which in turn returns the results to the RecipeService.  Those results travel through the WCF infrastructure back to the service proxy in the client.  The IngredientFinder filters the recipes for the requested ingredient and returns the results to the "Main" method which then writes the results back to the console for the user to read.

From a testing perspective, the only code we are responsible for in this system is Main, IngredientFinder, RecipeService and Business Objects.  The user and console are completely external to our application.  The service proxy is auto-generated by Visual Studio and the WCF framework is part of the .NET framework.  We do not need to test these parts.  Finally, the database is also a Microsoft product (SQL Server) and we can trust that it works correctly as well.  We need to eliminate all components in this system that we do not control, thus focus on testing the code in which we do control.

Eliminating the User and Console

Traditionally our "Main" method would instantiate our IngredientFinder (see part 1) with the service proxy, get the result and write it out to the console:

static void Main(string[] args)
{
    var finder = new IngredientFinder(new RecipeBoxServiceClient());

    foreach (RecipeData recipe in finder.GetRecipes(args[0]))
        Console.WriteLine(recipe.Title);

    Console.ReadKey();
}

The problem with this is that Console is a static class and cannot be replaced as-is.  Instead, we need to extract this as an interface and pass the interface in:

public interface IConsoleOutput
{
    void WriteLine(string line);
    ConsoleKeyInfo ReadKey();
}

private class ConsoleOutput : IConsoleOutput
{
    public void WriteLine(string line) { Console.WriteLine(line); }
    public ConsoleKeyInfo ReadKey() { return Console.ReadKey(); }
}

By doing this, we can now replace the Console.WriteLine static method with a spy in our test (later).  We will create a new static method named Execute which passes in the IConsoleOutput and IRecipeBoxService interfaces. Our new "Main" routine will look like this:

static void Main(string[] args)
{
    Execute(new ConsoleOutput(), new RecipeBoxServiceClient(), args);
}

public static void Execute(IConsoleOutput console, IRecipeBoxService service, string[] args)
{
    var finder = new IngredientFinder(service);

    foreach (RecipeData recipe in finder.GetRecipes(args[0]))
        console.WriteLine(recipe.Title);

    console.ReadKey();
}

Eliminating the WCF Infrastructure

Now that the user and console have been abstracted out, we can start thinking about testing the Execute method.  The problem we have now is that IRecipeBoxService is an automatically generated interface within the client's namespace.  We have an implementation of this interface in the service, but it is defined in the service's namespace.  The two interfaces are not compatible.  We want to eliminate the need for WCF, so we cannot use the generated proxy class.  What we need here is a bridge class:

public class ServiceWrapper : DataMining.RecipeBoxService.IRecipeBoxService
{
    private readonly Services.IRecipeBoxService _source;

    public ServiceWrapper(Services.IRecipeBoxService source)
    {
        _source = source;
    }

    private static ToType TranslateData<FromType, ToType>(FromType source) where ToType : class
    {
        var serverSerializer = new DataContractSerializer(typeof(FromType));
        var clientSerializer = new DataContractSerializer(typeof(ToType));

        using (var stream = new MemoryStream())
        {
            serverSerializer.WriteObject(stream, source);
            stream.Flush();
            stream.Position = 0;

            return clientSerializer.ReadObject(stream) as ToType;
        }
    }

    public DataMining.RecipeBoxService.RecipeData[] AllRecipes()
    {
        var result = new List<DataMining.RecipeBoxService.RecipeData>();
        foreach (var data in _source.AllRecipes())
            result.Add(TranslateData<Services.DataContracts.RecipeData, DataMining.RecipeBoxService.RecipeData>(data));
        return result.ToArray();
    }

    ...
}

This class bridges the client interface over to the service interface.  WCF interfaces are made up of Service Contracts and Data Contracts.  The Service Contract is the functional interface where the Data Contract defines what the data looks like.  The WCF framework uses the System.Runtime.Serialization.DataContractSerializer to transfer the data in plain-text.  In this ServiceWrapper class, the TranslateData function uses the DataContractSerializer to serialize the data to and from the client and service data types.  This class is the glue that replaces WCF from the testing process.  In a sense, this class is a very simple implementation of the WCF concepts.

Putting it all Together

The only piece of functionality still out of our control is the database.  I am going to replace the SQL Server with a temporal, in-memory database for testing.  This post will not go into the details, but my previous post on Blah Blah Blah talks about how to do this.  Now, our diagram looks shows a system where every piece of code is in our control:

App.Testing

Writing our Functional Tests

The only code we still need before we start writing functional tests is the ConsoleOutputSpy.  It captures the output in a list of strings that we can verify against.

public class ConsoleOutputSpy : Program.IConsoleOutput
{
    public List<string> Output { get; private set; }
    
    public ConsoleOutputSpy()          { Output = new List<string>(); }
    public void WriteLine(string line) { Output.Add(line); }
    public ConsoleKeyInfo ReadKey()    { return new ConsoleKeyInfo(); }
}

Now that we have our console output spy, we can look at our test SetUp:

private ConsoleOutputSpy _consoleOutput;
private DataMining.RecipeBoxService.IRecipeBoxService _service;

[SetUp]
public void SetUp()
{
    _service = new ServiceWrapper(new RecipeBoxService(new MockBackEndConfiguration()));
    _consoleOutput = new ConsoleOutputSpy();
}

Finally, we can write our tests.  This test will populate the mock database with four ingredients and three recipes.  Only two of the recipes include the "Cheese" ingredient, so we can test that our data miner will return only those two recipes that contain "Cheese":

[Test]
public void SimpleTest()
{
    AddIngredients("Macaroni", "Cheese", "Bread", "Peanut Butter");
    AddRecipeToDatabase("Mac & Cheese", "Macaroni", "Cheese");
    AddRecipeToDatabase("Grilled Cheese", "Bread", "Cheese");
    AddRecipeToDatabase("Peanut Butter Sandwich", "Bread", "Peanut Butter");

    Program.Execute(_consoleOutput, _service, new [] {"Cheese"});

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

Conclusion

This concludes my four-part series on testing WCF applications.  I have covered unit testing the service, unit testing the client, unit testing an asynchronous client and finally, functional testing the entire application. 

It is important for me to note that these techniques have served me very well in the real-world.  I have a Silverlight application which communicates asynchronously with a back-end WCF service.  These techniques have allowed us to write tests that cover our application from all aspects.  The tests we have in place run extremely quickly and are robust because they do not rely on any services running on a separate machine.

posted on Monday, January 05, 2009 9:56 PM

Feedback

# re: Testing WCF Service Apps (part 4 of 4) 1/10/2009 9:18 PM g59
Thank you for a very nice series. I am currently in the process of writing unit tests for a service that I rebuild from original code generated by Sympony. I do not have access to the server side code or the client (!?) so all I can do is test that the output from my calls exactly match the responses from the Apache hosted Symphony code... Your posts have been very helpful! Thanks alot!

In the above post you say:

"I am going to replace the SQL Server with a temporal, in-memory database for testing. This post will not go into the details, but my previous post on Blah Blah Blah talks about how to do this."

What is this mysterious blah blah blah post? ;) Is it the post on Castle ActiveRecord that you linked from a previous part of the series or is it some other post? I would love to read it :)

Thank you!

# re: Testing WCF Service Apps (part 4 of 4) 1/10/2009 9:18 PM g59
Thank you for a very nice series. I am currently in the process of writing unit tests for a service that I rebuild from original code generated by Sympony. I do not have access to the server side code or the client (!?) so all I can do is test that the output from my calls exactly match the responses from the Apache hosted Symphony code... Your posts have been very helpful! Thanks alot!

In the above post you say:

"I am going to replace the SQL Server with a temporal, in-memory database for testing. This post will not go into the details, but my previous post on Blah Blah Blah talks about how to do this."

What is this mysterious blah blah blah post? ;) Is it the post on Castle ActiveRecord that you linked from a previous part of the series or is it some other post? I would love to read it :)

Thank you!

# re: Testing WCF Service Apps (part 4 of 4) 1/20/2009 9:46 AM Brian Genisio
Yes, that is the same link. Thanks for pointing it out :)

http://houseofbilz.com/archive/2008/07/22/active-record-mock-framework.aspx

# re: Testing WCF Service Apps (part 4 of 4) 2/3/2009 3:39 PM Tor Hovland
This is good work!

We are also writing automated functional tests involving WCF services in our project. But we take a different approach. We think of the functional tests as integration tests, where the main purpose is to see that everything (except the GUI layer) fits together and works as a whole. Because of that, we don't want to take any shortcuts around WCF. If there were errors in the config files, for instance, your functional test will not catch them.

Granted, this does add some extra pain. We have to copy config files (post build task) for NUnit to find them, etc.

I guess it comes down to defining what the scope of your tests will be. You can't test everything. As I said, we don't test the GUI layer ourselves.

# re: Testing WCF Service Apps (part 4 of 4) 3/15/2009 2:38 AM Gil Zilberfeld
Hi Brian,

Nice series indeed. I wondered if there's a reason you don't use mocking frameworks here. I understand you want to isolate the dependencies (Console for example) but you seem to work a bit extra to get there.

Gil Zilberfeld




# re: Testing WCF Service Apps (part 4 of 4) 3/15/2009 12:28 PM Brian Genisio
Gil,

Thanks for the compliement AND for the question. I am glad to know that this series (which I spent quite a bit of time on) is being read, and I love questions. It makes me question my approaches.

In answer to your question, I did use mocks in parts 2 and 3 to show how to mock out the service from the client.

In part 4, I am showing a more functional test, and I wanted the data to come from the service directly, instead of a mocked out service.

There were three things I need to slip in here: the user, the WCF transport and the database. As for why I didn't use mocks, my answers are different for all three parts:

The Database -- This is way too hard to mock in this case since my service is using Castle ActiveRecord. The easiest way to mock the database in Castle ActiveRecord is to slip in an in-memory SQLite database that is destroyed after each test.

The WCF Transport -- This was much easier to write a bridge for than to use mocks, since the data between the client and the service had to flow.

The User -- This certainly could have been used with a mock framework like Moq or RhinoMocks (as I showed in parts 2 and 3). I just wanted to show a different approach, which is the "Spy" approach to mocking out the dependency. I should have made this more clear.

Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: