Sunday, May 16, 2010

NHibernate sessions in ASP.NET MVC



If you've used NHibernate in a ASP.NET MVC project you probably encountered the problem of keeping session open in the view in order to retrieve model data. (even though if session is open then the view actually makes calls to the db under the hood, which maybe is not very MVC. But that's another story)

The idea is to pass controllers an instance of a NHibernate session which is unique for the http request.

First off we need to "hijack" the ASP.NET mechanism that instantiate controllers to pass something in the constructor. In this case it's a ISession, but it could well be a repository class (which in turn gets a ISession passed in its constructor).

So a controller would look like this:

public class MyController : Controller
{
ISession session;

public MyController(ISession session)
{
this.session = session;
}
...

I've used Unity dependency container to manage the instantiation of controllers. ASP.NET MVC lets you do that by overriding the DefaultControllerFactory.

This is as simple as writing this class:

public class UnityControllerFactory : DefaultControllerFactory
{
UnityContainer container;

public UnityControllerFactory(UnityContainer container)
{
this.container = container;
}

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
IController controller = null;
if (controllerType != null)
{
controller = this.container.Resolve(controllerType) as IController;
}
return controller;
}
}

To make the session instance "singleton" for the http request, all I need to do is write a simple custom lifetime manager for Unity:

public class PerRequestLifetimeManager : LifetimeManager
{
public const string Key = "SingletonPerRequest";

public override object GetValue()
{
return HttpContext.Current.Items[Key];
}

public override void SetValue(object newValue)
{
HttpContext.Current.Items[Key] = newValue;
}

public override void RemoveValue() {} // this does not appear to be used ...
}

Let's get everything together in Global.asax. We need to register NHibernate ISession in Unity configuration. It should inject it using a factory method (OpenSession method of a ISessionFactory instance).

Then we need to tell MVC to use our Controller factory, passing the Unity container of our ASP.NET MVC application.

To close open NHibernate sessions we add some code in the Application_EndRequest.

Here's the code to put in our Global.asax file.

protected void Application_Start()
{
this.container = new UnityContainer();
ISessionFactory sessionFactory = NHConfigurator.CreateSessionFactory();
// NHConfigurator is my helper conf class

container.RegisterType(
new PerRequestLifetimeManager(),
new InjectionFactory(c => {
return sessionFactory.OpenSession();
}));

ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory
(this.container));

AreaRegistration.RegisterAllAreas();

RegisterRoutes(RouteTable.Routes);
}

protected void Application_EndRequest(object sender, EventArgs e)
{
Object sessionObject = HttpContext.Current.Items[PerRequestLifeTimeManager.Key];
if (sessionObject != null)
{
ISession currentSession = sessionObject as ISession;
if (currentSession != null)
{
currentSession.Close();
}
}
}

Hope this helps!

Saturday, April 10, 2010

Unit testing and the "constructors do nothing" rule



Introduction

Writing unit testing code is really easy.

It's not much more then writing some client code of the library you want to test and add some assert calls to check (test) if the code works as expected. With C# now we have lots of alternatives to do that.

But is it just that?

Not really!

Let's briefly look at some principles behind unit testing.

A unit test, as the name implies, should test just a unit a code. If it doesn't and it ends up depending on some other code then it becomes hard to tell why the test passes or not.
Also, depending on other code makes test hard to make repeatable. Just think about testing a method that depends on some database operation, now you should take care of maintaing database state over repeated calls to your test suite.

Being able to isolate units of code not also makes it more testable but also easier to work with and to refactor.

This post will try to explain some good practices to make your C# code more testable.

Constructors should do nothing

This is an important rule you should enforce if you want your code to be testable. It also makes it easier to enforce SRP (Single Responsibility Principle).
In the class constructor, objects should not be created, because if they are then your class becomes coupled to them and you are not able to inject testing objects during unit testing.

Lets jump to some sample code.


public class StringsCalculator
{
public int Sum(string x, string y)
{
int xi = 0;
int yi = 0;
int result = 0;
if (int.TryParse(x, out xi) && int.TryParse(y, out yi))
{
result = xi + yi;
}
else
{
throw new ArgumentException("Strings passed are not integers.");
}
return result;
}
}


This class StringsCalculator contains a method Sum that takes two strings and tries to sum them if the can be parsed as numbers. It clearly does two things, converting strings to integers and summing them. So if we want to test does separatly we should separate the two responsibilities. Perhaps separating the string conversion in a class StringToNumber:

public class StringToNumber
{
public int Convert(string stringNumber)
{
int result = 0;
if (!int.TryParse(stringNumber, out result))
{
throw new ArgumentException("String is not a number");
}
return result;
}
}

Thus we can refactor our StringsCalculator class:

public class StringsCalculator
{
StringToNumber stringToNumber;

public StringsCalculator()
{
this.stringToNumber = new StringToNumber();
}

public int Sum(string x, string y)
{
return stringToNumber.Convert(x) + stringToNumber.Convert(y);
}
}

This looks better, but what about our test:


[Test]
public void SumStringsNotTestable()
{
StringsCalculator ss = new StringsCalculator();
int result = ss.Sum("2", "3");
Assert.AreEqual(5, result);
}

It tests our class but this way it tests also the StringToNumber class so it's really not "unit" testing.

So let's follow the "Constructors do nothing" rule by doing some refactoring.

First off let's separate the two classes by putting a interface between them. In Visual Studio we just need to right click on the StringToNumber class and choose Refactor->Extract interface... and name it IStringToNumber. Next we need modify the StringsCalculator constructor to accept a parameter of type IStringToNumber.

public class StringsCalculator
{
IStringToNumber stringToNumber;

public StringsCalculator(IStringToNumber stringToNumber)
{
this.stringToNumber = stringToNumber;
}

public int Sum(string x, string y)
{
return stringToNumber.Convert(x) + stringToNumber.Convert(y);
}
}


Mocking objects

Now we can refactor our test to instantiate the StringsCalculator object by passing to its constructor a mock object and not a StringToNumber object. To do this we can use one of the many mocking frameworks for .Net around. I chose the very nice RhinoMocks library, that's easy to use and quite complete. So here's our new test:

[Test]
public void SumStringsTest()
{
MockRepository mocks = new MockRepository();
IStringToNumber stringToNumber = mocks.StrictMock();
StringsCalculator ss = new StringsCalculator(stringToNumber);
Expect.Call(stringToNumber.Convert("2")).Return(2);
Expect.Call(stringToNumber.Convert("3")).Return(3);
mocks.ReplayAll();
int result = ss.Sum("2", "3");
Assert.AreEqual(5, result);
mocks.VerifyAll();
}


As you can see we can instruct the RhinoMocks repository to expect two calls to the IStringToNumber. In this way we are able to verify the inner workings our our class, thus doing what is called white-box testing by calling the VerifyAll() method.

Constructor injection

Having all collaborators classes passed in as constructor parameters enables us to decouple nicely and test in isolation. But what about the client code using this classes, do they have to do lots of code to instantiate all those collaborators? Well there's a nice solution to this, dependency injection containers. In my case I'll use Unity, by using it to instantiate objects, client code doesn't have to prepare alkl those collaborators, Unity will take care of it, even in cascade if those collaborators themselves have other collaborators as constructor parameters. We can implement a integration test to verify our classes together:


[Test]
public void SumStringsUnityTest()
{
UnityContainer container = new UnityContainer();
UnityConfigurationSection section =
(UnityConfigurationSection)ConfigurationManager.GetSection("unity");

section.Containers.Default.Configure(container);

IStringToNumber stringToNumber = container.Resolve();
StringsCalculator ss = new StringsCalculator(stringToNumber);
int result = ss.Sum("2", "3");
Assert.AreEqual(5, result);
}


Unity configuration code could be conveniently located in a helper class.

So, to summarize, we can say: Constructors do nothing, all collaborators are passed in as parameters so we can mock them in unit tests and let a dependency injection mechanism compose them in client code.