Test all you queries to have handlers

CQRS architecture seems to be very popular. And I’m on that wave as well. In short CQRS is separating all read-actions from write-actions. And you have different classes for reading and writing. Queries for reading. Commands for writing.

For queries you would have IQuery and IQueryHandler interfaces:

public interface IQuery<out TResult>
{
}

public interface IQueryHandler<in TQuery, out TResult> where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

For command you would have very similar set of interfaces:

public interface ICommand
{
}

public interface ICommandHandler<in TCommand>
{
    void Handle(TCommand command);
}

This guy here talks about queries and mediator in more details, and I suggest you go read it if you want to use CQRS in your systems. But for cases like this where you have too many queries you should use Mediator pattern, where query handler is resolved inside of the mediator.

Mediator basically hides all the dependencies on query and command handlers from consumers: resolves the classes directly from DI container. The problem there that you might miss some handlers for some queries and then exceptions will fly at runtime.

To avoid that I suggest you put a test around that: check that all queries have all handlers:

class AllQueriesHaveHandlers
{
    [Test]
    public void AllQueries_Always_HaveHandlers()
    {
        //Arrange
        var allQueryTypes = Assembly.GetAssembly(typeof(IQuery<>)).GetTypes()
                .Where(t => t.IsClass && t.IsClosedTypeOf(typeof(IQuery<>)))
                .ToList();

        var allHandlerTypes = AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(a => a.GetTypes())
            .Where(t => t.IsClass && t.IsClosedTypeOf(typeof(IQueryHandler<,>)))
            .SelectMany(t => t.GetInterfaces())
            .ToList();

        // Sanity checks
        Assert.IsNotEmpty(allQueryTypes);
        Assert.IsNotEmpty(allHandlerTypes);

        // Act
        var missingHandlers = new List<Type>();
        foreach (var queryType in allQueryTypes)
        {
            var resultType = queryType.GetInterfaces()
                .Single(i => i.IsClosedTypeOf(typeof(IQuery<>)))
                .GetGenericArguments().Single();

            var proposedHandlerType = typeof(IQueryHandler<,>).MakeGenericType(queryType, resultType);
            var matchingHandler = allHandlerTypes.FirstOrDefault(t => t == proposedHandlerType);

            if (matchingHandler == null)
            {
                missingHandlers.Add(queryType);
            }
        }

        // Assert
        var missingNames = missingHandlers.Select(t => t.Name).ToList();
        var separator = String.Format("{0}{0}---------------{0}", Environment.NewLine);
        var finalMessage = separator + String.Join(separator, missingNames);
        Assert.IsEmpty(missingHandlers, finalMessage);
    }
}

This goes through your types and determines if you have all required handlers. Another way you can do the same test – to get container to resolve query handlers for all your queries. This is a bit more integration tests and probably will require database set up and accessed from your tests.

Here is the integration tests for the query handlers:

    [Test]
    public void Autoafac_CanResolve_AllQueryHandlers()
    {
        //Arrange
        var allQueryTypes = Assembly.GetAssembly(typeof(IQuery<>)).GetTypes()
                .Where(t => t.IsClass && t.IsClosedTypeOf(typeof(IQuery<>)))
                .ToList();

        // create your container
        var container = AutofacConfig.Configure();

        var errors = new List<String>();

        // Act
        foreach (var queryType in allQueryTypes)
        {
            var resultType = queryType.GetInterfaces()
                .Single(i => i.IsClosedTypeOf(typeof(IQuery<>)))
                .GetGenericArguments().Single();

            var handlerType = typeof(IQueryHandler<,>).MakeGenericType(queryType, resultType);
            try
            {
                container.Resolve(handlerType);
            }
            catch (Exception exception)
            {
                var resolutionException = exception as DependencyResolutionException;
                if (resolutionException != null)
                {
                    errors.Add(resolutionException.Message);
                }
                else
                {
                    errors.Add(exception.ToString());
                }
            }
        }

        // Assert
        var separator = String.Format("{0}{0}--------------{0}", Environment.NewLine);
        var finalMessage = separator + String.Join(separator, errors);
        Assert.IsEmpty(errors, finalMessage);
    }