Optimizing Entity Framework (2): Writing integration tests

Performance optimization is a highly iterative exercise, you will need to go into many cycles of “running the application, measure, optimize and re-run” until you reach a conclusion or get bored. To avoid the latter (getting bored), write integration tests.

Where to test?

Write you tests to invoke the code from the highest level outside the UI.

In an ASP.NET MVC aplication, that will be the controllers. Writing integration tests from the UI-level – although can be automated – is unnecessarily expensive, slow and will make repeating your tests less likely. Choosing to do it from a low level, for example, by invoking your Repository methods (or your EF code) directly can be extremely deceiving. I fell into that trap when writing my tests as I wrote them against the repository forgetting that my Controllers were actually calling a Service that was calling multiple repository methods through its lifetime, so I was not close enough to the real-life situation that I was trying to tackle.

So with the help of NUnit and Moq as my favorite test and mocking frameworks, here starts the journey:

[csharp language=”wraplines”]
[TestFixture]
public class ContactsTest : TestsBase
{
/// <summary>
/// The first call is very expensive and it can deceive us when doing the measurements
/// so let us warmup EF by making a call to the repository that generates the views
/// and then our tests
/// </summary>
[TestFixtureSetUp]
public void WarmupCodeFirstModel()
{
// EntityFrameworkProfiler.Initialize();
var contactsRepository = new ContactsRepository();
contactsRepository.ListContacts();
}

[Test]
public void ShouldReturnContactsInLessThan100Ms()
{
// 1. Arrange
var controller = GetController<HomeController>();
var stopWatch = Stopwatch.StartNew();

// 2. Act: Invoke the Contact action in HomeController
ViewResult result = controller.Contact();
var model = (List<ContactModel>) result.Model;
stopWatch.Stop();

//Assert
Assert.IsNotNull(model); //just a sanity check
Assert.Less(stopWatch.ElapsedMilliseconds, 100);

Debug.WriteLine("Integration test finished in {0} ms", stopWatch.ElapsedMilliseconds);
}
}
[/csharp]

The cost of the first call

The first-call to EF is the most expensive, it is caused partially by loading the metadata but mainly by the cost of generating the views. Quoting MSDN:

Before the Entity Framework can execute a query against a conceptual model or save changes to the data source, it must generate a set of local query views to access the database. Because of the high cost of generating these views, you can pre-generate the views and add them to the project at design-time …

Avoiding this one-time cost is not the objective of this post. But you can have a look at this msdn article for information on how to tackle it: How to Pre-Generate Views to Improve Query Performance.

So for our integration tests, in order to avoid this first-time cost ruining our measurements, we’ll make a first call in TestFixture setup in order to warm up our EF context, so that the upcoming calls don’t include this extra overhead that we don’t care about.

After that comes our test, the test calls the controller which will calls the Repository that will use EF to call the database. This means we exercise the whole stack of our application (except the UI). As a good practice and common pattern for MVC applications, controllers should have two constructors, one parameterless constructor that will be called by the real application and will create the the real dependencies (in this case an implementation of IContactRepository that uses EF), and another constructor that takes a parameter that will be used for unit-testing to pass a mock repository.

For our integration test, we want to test the real thing, therefore we call the parameterless constructor.

[csharp]
public class HomeController : Controller
{
private readonly IContactsRepository _repository;

public HomeController()
{
_repository = new ContactsRepository();
}

public HomeController(IContactsRepository repository)
{
_repository = repository;
}

public ViewResult Contact()
{
var model = _repository.ListContacts();
return View(model);
}
}
[/csharp]

Finally, getting HttpContext out of the way

Our integration tests are inheriting from TestsBase which provide us with some handy methods for our tests. The main issue we’re tackling here that our controllers – in any real application – will definitely make use of HttpContext for one reason or another (to access Profile, cookies or any other information about the Http Request). We have a generic method that returns a Controller that we will act on for our tests, the controller will make use of a mock HttpContext so that we can focus on the task at hand and forget about HttpContext.

[csharp]
public class TestsBase
{
/// <summary>
/// Returns an Controller of type T that uses a mocked Controller Context
/// that implements any dependencies on HttpContext that our controller needs
/// A variation of this method is very handy and useful for both integration tests and unit tests
/// </summary>
/// <typeparam name="T">The type of the Controller to return</typeparam>
/// <returns>Conroller of T</returns>
protected T GetController<T>() where T : Controller
{
var controllerMock = new Mock<T> {CallBase = true};
var controller = controllerMock.Object;
controller.ControllerContext = new ControllerContext(CreateContextBase(),
new RouteData(), controller);
return controller;
}

private HttpContextBase CreateContextBase()
{
// … basically, whatever you need to mock in order to get around your controller’s use of HttpContext
// This uses Moq which is my favorite mocking, but any other framework will do

var server = new Mock<HttpServerUtilityBase>();
var response = new Mock<HttpResponseBase>();
var request = new Mock<HttpRequestBase>();
request.Setup(r => r.UserHostAddress).Returns("127.0.0.1");
request.Setup(r => r.Url).Returns(new Uri("http://www.myurl.sd&quot;));
var session = new Mock<HttpSessionStateBase>();
session.Setup(s => s.SessionID).Returns(Guid.NewGuid().ToString());
//…. everything related to HttpContext is mocked

var context = new Mock<HttpContextBase>();
context.SetupGet(c => c.Request).Returns(request.Object);
context.SetupGet(c => c.Response).Returns(response.Object);
context.SetupGet(c => c.Server).Returns(server.Object);
context.SetupGet(c => c.Session).Returns(session.Object);

return context.Object;
}
}
[/csharp]

Summary

Don’t think of this exercise as a waste of time, imagine the alternative: every time, you have to open a browser, attach a debugger throw some StopWatches in the code and write the results in an excel sheet, compare, optimize and repeat. It’s a nightmare even with the smallest of applications. Now with these tests, you can run them in your test runner of choice, from console or even make them part of your builds. They are invaluable.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s