D3: Test Fixture & Using a Fake Context While Testing a WCF Service

First off, I’ll point out that D3 Release 0.1012 is now available on code gallery.  In addition to a foray into a custom, attribute-based text parser (which I’ll not discuss in detail here unless someone really wants me to since it has little bearing on the EF), this release includes the initial outline of the WCF service which should someday soon make it possible to actually have a working game.  In the course of putting that together, I continued to invest in testing improvements which will be the primary focus of this post.

Before going into those details, though, I have to commemorate the 4 year anniversary of DPMud’s first formal check-in.  One of the original members on the project, Josh Lindquist, sent me an email recently which includes a copy of the first auto-generated check-in mail for the project which had these headers:

From: Daniel Simmons
Sent: Friday, April 14, 2006 12:00 AM
Subject: [DPMUD] REDMOND\dsimmons: #9: Initial checkin of dpmud.

It’s hard to believe that 4 years later I’m still working on this crazy little project, but I am, and if anything I’m enjoying it more than ever! 

Enough nostalgia, now back to EF testability.

Simplifying Test Code with a Test Fixture

As we started filling in more tests for business logic on entities in the D3 model, I found that over and over I had code which setup several entities and relationships between them, put that data into a fake context and then used it to perform some action and verify the results.  This code was repetitive and prone to small errors.  In addition, because it was a bit of custom code each time, it was harder to look at a bunch of tests to pick out what was the core idea of the tests and what patterns were the same or different between them.  So I decided to centralize this code and configure my fake context in such a way that I could easily have a common set of initial data.

The first step was to add a new constructor to the D3DB object which I use to provide a simple IOC container.  Previously the only constructor was one which take an ID3Context instance, but now I’ve added a default constructor and a property on D3DB which is a Func used for creating a D3Context instance if needed:

 public static Func<ID3Context> CreateAction { private get; set; }

public D3DB() : this(CreateAction != null ? CreateAction() : null) { }

That way I could add a constructor to each of my test classes with the line:

 D3DB.CreateAction = () => new FakeD3Context();

and then the body of any tests needing to interact with the context would just be surrounded by:

 using (new D3DB())
{
    ...
}

It also means that I can add a static constructor to the real D3Context implementation which sets D3DB up to create a real context any time it isn’t overridden like in the test code above.

 static D3Context()
{
    D3DB.CreateAction = () => new D3Context();
}

The reason we have to configure this in a static constructor on the real D3Context rather than just in D3DB itself as a default value for the property is that D3DB is part of D3.Model so that entities can have a dependency on it, but that means it can’t have any dependency on any implementation of ID3Context.  So we define the basic mechanism in D3DB, setup the default configuration in D3Context and then override that configuration to point to a fake in the tests.

Once I had this mechanism in place, my fake context creation was centralized and I could expand on it by creating a test fixture with a standard set of test data.  I did that by adding a factory method to FakeD3Context:

 public static FakeD3Context CreateWithTestData()
{
    Room room1, room2;
    Exit exit1, exit2;
    Actor actor1, actor2;
    Player player1;
    Item item1, item2;
    User user1;

    var ctx = new FakeD3Context
    {
        Rooms = new FakeD3ObjectSet<Room>(
            (room1 = new Room { Id = 1, Name = "Room1", Exits = new List<Exit>() }),
            (room2 = new Room { Id = 2, Name = "Room2", Exits = new List<Exit>() })),
        Exits = new FakeD3ObjectSet<Exit>(
            (exit1 = new Exit { Id = 1, Name = "Exit1", SourceRoomId = 1 }),
            (exit2 = new Exit { Id = 2, Name = "Exit2", SourceRoomId = 2 })),
        Actors = new FakeD3ObjectSet<Actor>(
            (actor1 = new Actor { Id = 1, Name = "Actor1" }),
            (actor2 = new Actor { Id = 2, Name = "Actor2" }),
            (player1 = new Player { Id = 3, Name = "Player1" })),
        Items = new FakeD3ObjectSet<Item>(
            (item1 = new Item { Id = 1, Name = "Item1" }),
            (item2 = new Item { Id = 2, Name = "Item2" })),
        Users = new FakeD3ObjectSet<User>(
            (user1 = new User { Id = 1, Name = "User1" }))
    };

    room1.Exits.Add(exit1);
    room2.Exits.Add(exit2);

    exit1.TargetRoom = room2;
    exit2.TargetRoom = room1;

    actor1.Room = room1;
    actor2.Room = room1;

    player1.Room = room1;
    player1.UserId = user1.Id;

    item1.Room = room1;
    item2.Room = room1;

    return ctx;
}

and then I just modified the definition for D3DB.CreateAction set in the constructor for my test classes to call this factory method rather than constructing the fake context directly.  Viola!  Now each of my tests can automatically get a fake context with a small amount of fresh data available for each run. 

Oh yeah, and while I was at it I moved the FakeContext code and related bits into a new D3.TestUtils DLL so I could easily share this code across multiple test projects since I wanted to use it not only for the original purpose of testing the model but also in the new tests I created for the WCF service which takes player commands, parses them and then executes appropriate methods on the player object to carry out the action.  Which brings us to the next section…

Using a Fake Context to Test a WCF Service

Given the code above, it’s easy to create a fake context with some test data, call a method in the product that retrieves that context from the thread-local static property D3DB.Current and uses it, and then access the same fake context to verify appropriate results.  This is great, but what if I want to write tests for a method that creates its own context instance rather than assuming one has been created in the surrounding environment?  Given that the normal pattern for my WCF service methods is to create a context instance at the start of each method and dispose of it at the end so that the service calls can be independent and essentially stateless, this is a particularly relevant question. 

Not only do I need to intercept the creation of the context in order to make sure it’s a fake (easily accomplished given the new D3DB default constructor and CreateAction method described above), but I also need to be able to get access to that same context instance at least after the method call has completed so that I can validate the results and maybe even beforehand in order to do some special initialization.  For this second trick, I expanded on D3DB’s default constructor so that constructing a new D3DB instance with the default constructor will just re-use any existing context rather than creating a new one or throwing an exception.  Since the D3DB objects are disposable, as long as you put them in a using block, constructing a new D3DB when one is already active can only happen in a way that is completely nested so that the inner one will dispose before the outer one.  So this process is basically safe, and the overall pattern should be familiar if you have worked with TransactionScope or something of the kind.

The resulting code for the D3DB default constructor looks like this:

 public D3DB()
{
    if (current == null)
    {
        if (CreateAction == null)
        {
            throw new InvalidOperationException("Must set a CreateAction if you are going to use the default constructor.");
        }

        Current = CreateAction();
    }
    else
    {
        disposeNeeded = false;
    }
}

And the code for the dispose pattern undergoes a corresponding modification like this:

 private bool disposeNeeded = true;

public void Dispose()
{
    if (disposeNeeded)
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

The result of all this is that I can write a WCF service method whose body is surrounded by a using block which creates a new D3DB instance using the default constructor and that method will create the real context when running in the program directly, but I can also create a test method whose body has a using block creating a new D3DB instance with the result that it will create a fake context, give me an opportunity to interact with it if necessary, and then let me call the service method whose D3DB constructor call will see that a context is already available and just use that rather than making another.  Since this same context is used for the whole test, I can then also verify that the service call had the intended effect upon the database.

ExpectedExceptionEx

For my last testing trick of the day, let me tell you about a small new addition to my testing arsenal: ExpectedExceptionExAttribute.  The motivating scenario for me was that I added a couple new constraints to the database to enforce that actor and user names were unique even though they aren’t part of the primary key for the entity. 

Adding those constraints was easy—I just put a couple additional calls to ExecuteStoreCommand in the database creation code on the context, but the more difficult part comes when testing them.  it’s easy enough to create two actors with the same name, try to save them both to the database and verify an exception is thrown, but what if that exception was actually the result of some other, subtly different thing and in the end the intended behavior wasn’t being enforced correctly?  I really needed a way to verify more than just that an exception was thrown of a particular type—especially since a broad class of failures when saving changes to the database all result in an “UpdateException” being thrown.

Happily, the MsTest library has a great extension point for just this sort of thing.  I was able to create a new attribute which I can place on my tests that does more than just verify the exception type, it can also optionally check the inner exception type and/or search for strings within the exception message or inner exception message.  So now my test for one of the new constraints looks like this:

 [TestMethod, ExpectedExceptionEx(typeof(UpdateException), InnerExceptionType = typeof(SqlException), 
    InnerExceptionMessageContains = "Violation of UNIQUE KEY constraint")]        
public void Users_Name_MustBeUnique()
{
    using (var ctx = Utilities.CreateTestContext())
    {
        ctx.Users.AddObject(new User { Name = "foo" });
        ctx.SaveChanges();

        ctx.Users.AddObject(new User { Name = "foo" });
        ctx.SaveChanges();
    }
}

The entire code to enable this new exception looks like this:

 namespace D3.TestUtils
{
    using System;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [AttributeUsageAttribute(AttributeTargets.Method, AllowMultiple = false)]
    public sealed class ExpectedExceptionExAttribute : ExpectedExceptionBaseAttribute
    {
        public Type ExceptionType { get; private set; }

        public Type InnerExceptionType { get; set; }
        public string ExceptionMessageContains { get; set; }
        public string InnerExceptionMessageContains { get; set; }

        public ExpectedExceptionExAttribute(Type exceptionType)
        {
            ExceptionType = exceptionType;
        }

        protected override void Verify(Exception exception)
        {
            Assert.IsNotNull(exception);

            RethrowIfAssertException(exception);
            Assert.IsInstanceOfType(exception, ExceptionType);

            if (InnerExceptionType != null)
            {
                Assert.IsInstanceOfType(exception.InnerException, InnerExceptionType);
            }

            if (ExceptionMessageContains != null)
            {
                Assert.IsTrue(
                    exception.Message.Contains(ExceptionMessageContains), 
                    String.Format("ExceptionMessage does not contain '{0}'.  Message value: {1}", 
                                   ExceptionMessageContains, exception.Message));
            }

            if (InnerExceptionMessageContains != null)
            {
                Assert.IsTrue(
                    exception.InnerException.Message.Contains(InnerExceptionMessageContains),
                    String.Format("InnerExceptionMessage does not contain '{0}'.  Message value: {1}", 
                                   InnerExceptionMessageContains, exception.InnerException.Message));
            }
        }
    }
}

That’s it for today.  Have fun playing with the EF4 final bits and testing your code as you go.  If you have any questions or feedback, as always I’d love to hear it.

- Danny