Sunday, 21 September 2014

Unit Testing Entity Framework with Effort

Previous posts in this series

Introduction

In my previous post I discussed steps I've been taking with recent application to move more logic and behaviour into a richer domain model. This has a number of advantages, a significant one being ease of testing of this behaviour. As this is coded as methods on a standalone model class, it's very easy to test due to it's lack of dependencies. Nothing needs to be mocked or stubbed; an instance can just be instantiated in the test method and the appropriate methods called and results asserted.

Given the CQRS style approach I've taken elsewhere in the applications, MVC controllers are fairly light and have less value for testing. The main part of the application where logic lies is in the individual query and command handlers. These have a tight dependency on Entity Framework though so are significantly harder to test.

Approaches to Unit Testing Entity Framework

One track I expect a lot of developers have started down is to attempt to mock out the EF context, allowing you to return a known in memory collection or instance instead of hitting the database for your tests. EF 6 provides some good support for this. This is certainly fast, but the downside with this is that querying using LINQ to Objects is not the same as querying LINQ to Entities. There's a good degree of overlap so still some value in this, but writing tests this way runs the risk of having tests that pass in your tests but fail in the application.

Another approach is to simply use the database itself. Most people wouldn't class this a unit test any more, rather an integration one, as it suffers from two downsides. One is that it's going to be a lot slower than using an in-memory data store and the second that it's hard to ensure your data remains consistent. If one test changes the data, you have to make sure you have a fresh schema and data situation restored for further tests, which can take quite a bit of effort to maintain.

Introducing Effort

Effort is an open-source provider that generates an in-memory database from your EF model, and accurately represents (almost) all EF querying operations. Thus in each test you can instantiate a clean data store and run your tests against it. In my case this meant asserting that my queries return the correct results and view models, and commands persist the data as expected.

The big advantage here therefore is removing the brittleness of the tests in ensuring you have a known base schema and data to start each test. It's also fairly fast - I say fairly, as I still find half a second or so for each test, which isn't much but adds up of course if you have a lot.

One other small downside is the "almost" note above. There are some EF operations you can run - within SqlFunctions for example for date operations - that are SQL Server specfic. Effort attempts to remain database agnostic, so can't handle the use of these functions. I get round that by passing a flag indicating whether SQL specific functions are available to the query handler - if they are, as in the application itself, they are used. In the test they are not. So there's a small difference in behaviour between my tests and the real-world application - but for my application at least this is pretty insignificant.

Effort Example

There's good documentation on the Effort Codeplex site for it's set up and use, including seeding data from code or CSV files. I'll just note here some aspects I had to work around slightly differently - in particular as I am using ASP.Net Identity, which means the constructors available for "standard" EF aren't available and some modification is required. Many thanks to the author of the library for his help with this.

Firstly, here's an example of a test. This one is testing a query handler - confirming that given a particular query, the correct view model is populated and returned.

[TestMethod]
public void SurveyQuestionViewModelQueryHandler_WithValidQuery_ReturnsExpectedResults()
{
    // Arrange
    SetUpContextAndTestData();
    var responseId = CreateAndPersistResponse();

    var handler = new QuestionViewModelQueryHandler(Context);
    handler.SqlSpecificFunctionsAvailable = false;
    var worker = Context.OrganisationWorkers.Single(x => x.LastName == "Worker");
    var questionId = Context.Questions.Single(x => x.Code == "A6").Id;

    var query = new QuestionViewModelQuery {
        UserId = worker.Id,
        ResponseId = responseId,
        QuestionId = questionId,
    };

    // Act
    var result = handler.Retrieve(query).Result;

    // Assert
    Assert.AreEqual(questionId, result.QuestionId);
    Assert.AreEqual("Which of these are vegatables?", result.QuestionText);
    Assert.AreEqual("Section 1", result.SectionName);
    Assert.AreEqual(4, result.AnswerOptions.Count());
    
    Assert.AreEqual(0, result.SurveyProgressPercent);
    Assert.AreEqual(42, result.SectionProgressPercent);
}

The first step is to set up the in-memory context using the Effort library via the SetUpContextAndTestData() method, which I'll expand on in a moment. This creates the schema and a set of base data required for all tests. After that we call a helper method to instantiate a single survey response object (an entity in my application) that we need for this specific test.

The rest of the Arrange part of the test is involved with instantiating a handler object, setting the property on the handler that indicates of SQL specific functions can be called, and looking up some values from the in-memory database to create a query object.

The single line in the Act simply calls the Retrieve method on the handler and retrieves the result. In the Assert section we call several asserts, to ensure the resulting view model is populated as we are expecting.

Test Setup

Going back to the arrange steps, the following code is used to instantiate the in-memory database using the Effort library.

protected void SetUpContextAndTestData()
{
    InitContext();
    TestDataSeeder.SeedData(Context, true);
}

private void InitContext()
{
    var dataFolder = AppDomain.CurrentDomain.BaseDirectory;

    var connection = Effort.DbConnectionFactory.CreateTransient();
    var dummyContext = new ApplicationDbContext();
    var builder = new DbModelBuilder();

    var m = typeof(ApplicationDbContext).GetMethod("ConfigureModel", BindingFlags.NonPublic | BindingFlags.Instance);
    m.Invoke(dummyContext, new object[] { builder, false });

    var model = builder.Build(connection).Compile();

    Context = new ApplicationDbContext(connection, model, false);
    Context.Configuration.AutoDetectChangesEnabled = true;
}

The call to InitContext sets up the schema using a variation of the standard Effort instantiation to work with the constructors available in ASP.Net Identity. A dummy context is first created and then the Effor in-memory model is built from that. Please note if you aren't using ASP.Net Identity then you should follow the set-up code as detailed on the Codeplex site.

We then make a call to a method responsible for seeding the data. This actually leverages the existing code for seeding data following an EF migration. It passes a flag to indicate to the seeding method whether on not to call any SQL server specific migrations. For example I had some code to override conventions for various date fields to use the smalldatetime data type - as this is SQL specific, Effort won't be able to work with this. But as this setting isn't relevant for tests, it can safely be ignored.

Conclusion

I'd certainly recommend developers looking to test EF methods take a look at Effort. Pun intended, in using it there's not much effort involved in getting the parts of your application that traditionally would be quite hard to test, under your unit test coverage.

Sunday, 14 September 2014

Rich Domain Models and Entity Framework

Previous posts in this series

Working with a Rich Domain Model and Entity Framework

As described in previous posts in this series, I'm documenting a few changes to the way I've recently been building out ASP.Net MVC applications, following various influencers from blogs and books. One area I was keen to look at was creating a rich domain model - inspired by the Eric Evans classic book and some posts by Julie Lerman and Jimmy Bogard.

Previously I would have worked more with what is known as an anaemic domain model - which isn't quite as bad as it sounds... but means that when creating entities for use in Entity Framework Code First, they would in the most part be made of a simple set of properties with public getters and setters. With a rich domain model, we look to push more behaviour into these model classes and provide tighter means of control over their use by calling code from the rest of the application.

There are a number of means to do this - in this blog post I describe a number I've used.

Control via constructors

One first method is to remove the parameterless constructor that you might otherwise have, thus preventing calling code from simply newing up an instance of the class, without necessarily setting some required properties. In fact you can't completely remove it - EF requires it - but you can make it private.

Once that's in place you can create one or more constructors that will enforce the calling code to provide whatever properties are deemed required and thus not create what might by the terms of the application be deemed an invalid object. Here's an example from a survey application that defines an entity for a question:


    private Question()
    {
        Options = new List<QuestionOption>();
    }

    public Question(string code, string text, int orderInSection,
        QuestionSection section, QuestionType type)
        : this()
    {
        Code = code;
        Text = text;
        OrderInSection = orderInSection;
        Section = section;
        Type = type;
    }

Control via properties

A second step to control more fully how your class is used, is to make the property setters private. This will prevent calling code from simply setting values for them, instead you provide methods where appropriate groups of properties can be set together, and validation can be applied.

This example from the same application controls the setting of an allowable range for a numeric question's answer:


    public int? NumericalMin { get; private set; }

    public int? NumericalMax { get; private set; }

    public void SetNumericalRange(int min, int max)
    {
        if (min > max)
        {
            throw new ArgumentException(“Max parameter must be greater than the min parameter.");
        }

        NumericalMin = min;
        NumericalMax = max;
    }

Logic in the model

The last method to discuss involves pushing more of the application's logic into the rich domain model, rather than having this in various services or other components of the application. There's some major advantages to this, particularly when compared to making additional database requests to handle particular behaviours. The code is arguably easier to write and maintain, and certainly easier to test as you can simply create instances of the classes and test the behaviour methods.

You do need to take care though - in order to fulfil certain behaviours it might be necessary to instantiate your object from the database with a more fully populated object graph that you otherwise normally would. So it's important to bear in mind the database requests that your method will require.

This final example illustrates that balance and why it's certainly worth looking to move behaviour into model methods when you can. With the survey application, we have multiple choice questions that you can obtain a score from based on which options you select in your answer. I required a means of calculating the maximum score available on a question.

For a multiple select question with check-boxes, the maximum score would be the total associated with each of the options. For a single select though, where you can only select one via radio buttons, the maximum score would be the option that had the highest score associated with it.

The following code examples illustrate how this logic can be set up as a method of the Question class, which will be valid so long as the related Options for the question are also loaded. If that is the case though, the code is very easy to write, maintain and test.


    public int GetMaximumAvailableScore()
    {
        if (Type.Id == (int)QuestionTypeId.MultipleSelect)
        { 
            return Options.Sum(x => x.Score);
        }
        else if (Type.Id == (int)QuestionTypeId.SingleSelect)
        {
            return Options.Max(x => x.Score);
        }
        else
        {
            return 0;
        }
    }

    [TestMethod]
    public void Question_GetMaximumAvailableScoreForMultiSelect_ReturnsCorrectValue()
    {
        // Arrange
        var question = CreateMultiSelectQuestion();

        // Act
        var maxScore = question.GetMaximumAvailableScore();

        // Assert
        Assert.AreEqual(8, maxScore);
    }