Introduction
While writing Unit Tests for an application with a complex business logic, I came across some classes that were exposing very few public
methods, and had a bunch of private
methods. Since the core logic was mostly embedded in those private
methods, I thought I couldn't skip them in the tests. So I started to seek for a way for invoking them in my Test project. I found out that MsTest already has some shortcuts for invoking private
methods (namely the PrivateObject
and the PrivateType
classes). Unfortunately, I was already familiar with NUnit and didn't want to switch to another testing framework in the middle of the development. I was tempted to mark the private
methods as internal
, since the methods with that access modifier can be invoked from outside the assembly they reside, if one marks the assembly itself with the InternalsVisibleTo
attribute, in the following way (code from AssemblyInfo.cs):
[assembly: InternalsVisibleTo("Business.Tests")]
Background
After having replaced some modifier from private
to internal
, I soon felt some kind of frustration, since I saw that I was widening the scope of those members only for my tests. So I thought that Reflection could be a better way for accomplishing that task. Here is a simple example of a private
method to be tested:
public class Range<T> : IRange<T> where T : IComparable<T>
{
public T Start { get; set; }
public T End { get; set; }
private static bool Overlaps(IRange<T> first, IRange<T> second)
{
return first.Start.Between(second) || first.End.Between(second);
}
}
And here's the Test
method that uses Reflection for invoking it (it uses NUnit version 2.5.10):
[Test,
Description("Tests two ranges that don't overlap.")]
public void OverlapTest([Random(0, 1000, 5)] int firstStart,
[Random(1001, 2000, 5)] int firstEnd,
[Random(2001, 5000, 5)] int secondStart,
[Random(5001, 10000, 5)] int secondEnd)
{
var methodInfo = typeof(Range<int>).GetMethod
("Overlaps", BindingFlags.NonPublic | BindingFlags.Static);
var result = methodInfo.Invoke(null, new[]
{
new Range<int>(firstStart, firstEnd),
new Range<int>(secondStart, secondEnd)
});
Assert.False((bool)result);
}
I knew from earlier experiences how a naive use of Reflection can dramatically slow down the procedures that rely on it. In the particular case, the use of random values as parameters results in multiple calls to the Test
method, and subsequently to the Type.GetMethod
method. Now, one could also ignore performance issues on Unit Tests, but here there was also a verbosity issue. Using the above syntax results in a lot of "ceremonial" code. So I began considering to use a new feature of C# 4, namely the dynamic
keyword and the DynamicObject
class. I had just finished the reading of the nice article Understanding the Dynamic Keyword in C# 4 that shows some examples of how the COM methods of Microsoft Excel get called more concisely using the dynamic
keyword. I thought it might be useful for my unit tests too. So I wrote the DynamicObjectBase
class:
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
namespace Business.Test
{
public class DynamicObjectBase: DynamicObject
{
private readonly Type _type;
private readonly Object _instance;
private readonly Dictionary<string, MethodInfo> _methods =
new Dictionary<string, MethodInfo>();
public DynamicObjectBase(Object instance)
{
_instance = instance;
_type = instance.GetType();
}
public DynamicObjectBase(Type type)
{
_instance = null;
_type = type;
}
public override bool TryInvokeMember
(InvokeMemberBinder binder, object[] args, out object result)
{
if (!_methods.ContainsKey(binder.Name))
{
var methodInfo = _type.GetMethod(binder.Name,
BindingFlags.NonPublic | BindingFlags.Static |
BindingFlags.Instance | BindingFlags.Public);
_methods.Add(binder.Name, methodInfo);
}
var method = _methods[binder.Name];
result = method.Invoke(method.IsStatic ? null : _instance, args);
return true;
}
}
}
Using the Code
The code above overrides the TryInvokeMember
method, that gets called every time a method is invoked on an instance of DynamicObjectBase
declared as dynamic
.
The class uses a cache of MethodInfo
objects, and it accepts in its constructors either the instance or the type of the class where to seek the private
methods.
If a class contains only static
methods, we can call the constructor that accepts a Type
instance. Instead, if we are going to test instance private
methods, it is mandatory to use the constructor that accepts Object
as a parameter. In the Test
class, we can create an instance of DynamicObjectBase
and invoke the private
methods on it, whether instance
or static
they are. So the Type.GetMethod
method gets called only once, and in the next calls it uses the cached instance of MethodInfo
. However, the call to MethodInfo.Invoke
remains, and it's the most expensive in term of CPU load. So the performance test shows only a very little gain in the execution time. But we've stripped away the messy reflection code from the test, so look how less verbose is the calling code in the rewritten Unit Test:
private readonly dynamic _range;
public RangeFixture()
{
_range = new DynamicObjectBase(typeof(Range<int>));
}
[Test,
Description("Tests two ranges that don't overlap.")]
public void DynamicOverlapTest([Random(0, 1000, 5)] int firstStart,
[Random(1001, 2000, 5)] int firstEnd,
[Random(2001, 5000, 5)] int secondStart,
[Random(5001, 10000, 5)] int secondEnd)
{
var result = _range.Overlaps(new Range<int>(firstStart, firstEnd),
new Range<int>(secondStart, secondEnd));
Assert.False(result);
}
Points of Interest
The DynamicObject
class fits well not only for interacting with objects that require a runtime type checking (once called "late binding"), but also for hiding some verbose Reflection code that dynamically invokes the members of a class. It is good for writing concise and readable Unit Tests, like in the scenario depicted above.
History
- 2011 August, 30 - First release
- 2011 August, 31 - Simplified the example code