A quick and dirty XUnit/AutoFixture MongoDb test harness

September 21, 2015 @ 6:37 am Posted to .Net, Testing by Antony Koch

As part of my new journey into outside-in testing I’ve – of course, for those that know me – looked into the tooling aspect. A part of that is implementing a MongoDB test harness injected via AutoFixture’s XUnit plugins.

As a quick side note, I think tooling is critical to elevating ones self from being a good developer to becoming a great one, as it removes the need to focus on anything other than writing production code. An example of this is my usage of NCrunch for continuous test execution. I can’t extol the benefits of not stopping to run all my tests enough, and it’s ben great to see the ongoing development of NCrunch since it’s free days.

Anyway – back to the point at hand.

I am building a MongoDb-backed application outside of my regular 9-5 engagement with my banking client, Investec, and needed a quick way to access the db in my tests. Avoiding ceremony was critical as I’m really into working on the production code as much as possible, not wasting time writing tests that will need to be rewritten when my implementation invariably changes as I learn more about the system I’m building. My app involves a twitter login, meaning everything I do from a database perspective is best served using the twitter UserId field. For cut one, I’ve come up with the following code, which I’ve added comments to for clarity:

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Configuration;

using MongoDB.Bson;
using MongoDB.Driver;

namespace Amko.TwendingToday.Fast.Mongo
{
    public class MongoDbHarness : IDbHarness
    {
        private IMongoDatabase _db;

        public MongoDbHarness()
        {
            // Extract connection string from config
            var connectionString = ConfigurationManager.AppSettings[ServiceConstants.MongoDbUri];

            // Get a thread-safe client object by using a connection string
            var mongoClient = new MongoClient(connectionString);

            // Get a reference to a server object from the Mongo client object
            const string databaseName = "twitterapp"; // I've taken the real name out here to avoid giving spoilers :)

            // bootstrap our DB object
            _db = mongoClient.GetDatabase(databaseName);

        }

        public async Task<BsonDocument> GetAsync(string collection, string userId)
        {
            // Extract collection as a set of BsonDocument. The production code deals in
            // concrete domain objects, but for test purposes I'm loathe to spend time
            // building and rebuilding the ever changing domain in my test code. It
            // doesn't offer much over string indexes
            var coll = _db.GetCollection<BsonDocument>(collection);

            // build a filter for the get
            var filterDefinition = Builders<BsonDocument>.Filter.Eq("UserId", userId);

            // pull out the matching docs asynchronously
            var list = await coll.Find(filterDefinition).ToListAsync();

            // return the first one
            return list.FirstOrDefault();
        }
    }

    public interface IDbHarness
    {
        Task<BsonDocument> GetAsync(string collection, string userId);
    }
}

Although I’ve mentioned it in the above comments, I think it’s worth mentioning the point regarding the test domain and my decision not to create it. It’s common to build a copy of the domain object in test code to deserialise database into, or in worse cases to use the production models, but I decided against this. I feel that for it to be a true outside in test I should express my query against the resultant JSON from the DB as I might query the JSON in the real world, as it’s an extra verification step against how I think the system is working.

The injection of this is handled by AutoFixture using the following specimen builder:

    public class MongoHarnessSpecimenBuilder : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            var type = request as Type;

            if (type == null || type.IsPrimitive)
            {
                return new NoSpecimen(request);
            }

            if (type == typeof(IDbHarness))
            {
                return new MongoDbHarness();
            }

            return new NoSpecimen(request);
        }
    }

And this allows me to build my tests like this:

        [Scenario]
        [StructuremapAutoData]
        public void TracksUserWhenTheyLogin(
            AuthController sut, 
            ActionResult actionResult,
            AuthenticatedUser user,
            IDbHarness dbHarness,
            DateTime now,
            string returnUrl)
        {

I’ll dig out some resources for building the AutoFixture framework backing my AutomapData attribute and post them here later.

Leave a Reply

Your email address will not be published. Required fields are marked *