Showing posts with label TDD. Show all posts
Showing posts with label TDD. Show all posts

Thursday, November 14, 2013

Simple TypeMock Auto-Mocking container

TDD is a great methodology for simplifying your code and focusing on delivering business value. When doing TDD, we always want to strive to make it as friction-free as possible. This means making writing tests and, more importantly, refactoring as painless as we can. One of the ways we can do that is to use an Auto-Mocking container.

A very common reason for tests to change is because we added a new dependency to our SUT and we have to modify all of our previous tests to use this new dependency. One way to avoid this is to use an object that will build your SUT and inject all appropriate dependencies as mocked objects. So your tests go from looking like this:

dependency1 = new Mock<IDependency1>();
dependency2 = new Mock<IDependency2>();
sut = new MyClass(dependency1, dependency2);

to this:

mocker = new Automocker();
sut = mocker.CreateSut<MyClass>();
dependency1 = mocker.GetMock<IDependency1>();
dependency2 = mocker.GetMock<IDependency2>();

We can now add another dependency without having to modify this test (assuming that this dependency is not used in this test).

Most of the popular mocking frameworks that are in use today (Moq, Rhino.Mocks, NSubstitute, etc) all have 1 or more Auto-Mocking frameworks that can be downloaded via NuGet. On our current project, we are using TypeMock, which is a very powerful, commercial grade, mocking tool. It can mock out almost anything and is used quite extensively in the SharePoint development world. There is no Auto-Mocking container for it, so I decided to write one.

An Auto-Mocking container needs to be able to do 2 things. Generate a SUT object with all dependencies mocked out and be able to retrieve the dependencies that were injected. TypeMock uses the following method to generate a mock object

Isolate.Fake.Instance<IDependency>();

Since it uses a static generic method, there is no way to call this method with a Type object without using reflection. So we will use reflection to call this method. We can get the MethodInfo object for this method in the following way:

TypeMockInstance = Isolate.Fake.GetType()
        .GetMethods()
        .Where(m => m.Name == "Instance")
        .Select(m => new
             {
              Method = m,
              Params = m.GetParameters(),
              Args = m.GetGenericArguments()
             })
        .Where(x => x.Params.Length == 0
           && x.Args.Length == 1)
        .Select(x => x.Method)
        .First();

and we would use it like so:

TypeMockInstance.MakeGenericMethod(type).Invoke(Isolate.Fake, null);

When creating our SUT we need to use reflection to get the types of the constructor parameters and use the method above to create them. In order to retrieve them at a later point we need to create a registry, which will simply be a Dictionary<Type, object>; In the case of multiple constructors we will pick the largest constructor. Here's what our CreateSut method looks like:

public T CreateSut<T>()
{
 var type = typeof (T);
 var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
 //get largest constructor
 ConstructorInfo largestConstructor = null;
 foreach (var constructor in constructors)
 {
  if (largestConstructor == null ||
   largestConstructor.GetParameters().Length < constructor.GetParameters().Length)
  {
   largestConstructor = constructor;
  }
 }
 //generate mocks and build up parameter list
 var parameters = new List<object>();
 foreach (var parameterInfo in largestConstructor.GetParameters())
 {
  if (!_typeRegistry.ContainsKey(parameterInfo.ParameterType))
  {
   _typeRegistry[parameterInfo.ParameterType] = GetMockInstance(parameterInfo.ParameterType);
  }
  parameters.Add(_typeRegistry[parameterInfo.ParameterType]);
 }
 return (T) largestConstructor.Invoke(parameters.ToArray());
}

To get our generated mock objects we can grab them from our dictionary after our SUT has been created. The full source for the Automocker can be found on Github.

Friday, May 14, 2010

NInject, NHibernate, and unit testing

I recently came across an interesting problem in my tests. In the application I am working on, I have recently created a framework for generating tag numbers for pieces of equipment. I put unit tests around the code and decided that the system was complex enough to warrant some integration tests.

The set up for the integration tests is quite complex as I have to not only set up an NHibernate session, but also the Ninject kernel. In my app the way I bind the NHibernate session is through the following line:

Bind<ISession>().ToMethod(c => NHibernateSessionFactory.Instance.OpenSession()).InRequestScope();

In my integration test SetUp I create a session for the test to use and start a transaction. In the TearDown I rollback the transaction and dispose the session. So I needed to manually change the binding to ISession in Ninject. I did that using the following code

foreach (var binding in _kernel.GetBindings(typeof (ISession)))
{
 _kernel.RemoveBinding(binding);
}
_kernel.Bind<ISession>().ToMethod(c => _session).InRequestScope();

(I would love if anyone suggested a better way to do this)

The integration test was working fine. Until I started working on another completely unrelated module for doing auditing on certain changes to domain objects. Once the module was finished I ran the entire build again and the integration tests started failing with a message that the ISession object was closed. If they were run independently or even as part of an entire integration tests run, they passed. I finally figured out that they would only fail, when they were being run together with the tests for this new module.

The culprit turned out to be this line of code I was running in the TestFixtureSetUp for the auditing tests:

HttpContext.Current = new HttpContext(new HttpRequest(string.Empty, "http://domain.com", string.Empty), new HttpResponse(new StringWriter()));

Since the HttpContext.Current was staying the same, the integration tests were only ever providing one ISession instance for all of the tagging integration tests. So once the first test ran and disposed of the session, any other components that required an ISession were still getting the old disposed instance of the ISession. The fix was simple, I just had to set HttpContext.Current back to null in the TestFixtureTearDown for the audit module test.