Testing private members with PrivateObject and PrivateType
Posted on October 8, 2012
I’m currently working on a legacy project that has been ported from Visual Studio 2010 to Visual Studio 2012. The project uses the Visual Studio 2010 feature that allowed to access private members through some automatically generated code.
In Visual Studio 2012, this causes a warning when compiling:
This task to create private accessor is deprecated and will be removed in a future version of visual studio
I’ve been looking around to find a solution. The right solution would obviously be to refactor the code in a way that testing private methods is not necessary, but that is not (currently) an option in the solution I’m working on. In stead, I’m relying on two new classes in the .net framework : PrivateObject and PrivateType.
The PrivateObject can be used to access private methods, properties, fields where the PrivateType class can be used to access static members.
I have made a simple test project to show the features. The adder.cs class contains a public method as well as some private fields and private static methods. Don’t mind that the code doesn’t make sense as it is only for demo purposes.
using System.Collections.Generic; using System.Linq; namespace PODemo { public class Adder { private int a; private int b; private int result; public void Add(int one, int two) { a = one; b = two; result = AddNumbers(one, two); } private static int AddNumbers(int one, int two) { return one + two; } private static int AddNumbers(IEnumerable numbers) { return numbers.Sum(p => p.Value); } } public class Number { public int Value { get; set; } } }
The Add method doesn’t return the result. Suppose I would like to test the functionality by verifying the result field:
[TestMethod] public void PrivateFieldsTest() { var test = new Adder(); var po = new PrivateObject(test); test.Add(2, 4); Assert.AreEqual(2, po.GetField("a")); Assert.AreEqual(4, po.GetField("b")); Assert.AreEqual(6, po.GetField("result")); }
I create a new Adder object and then creates a PrivateObject that wraps the test object. The I can use the GetField methods to access private fields. Also, GetProperty and Invoke methods are available.
Suppose I would like to test the static methods, I can use the PrivateType object instead.:
[TestMethod] public void PrivateStaticMemberTest() { var pt = new PrivateType(typeof (Adder)); var result = (int) pt.InvokeStatic("AddNumbers", BindingFlags.NonPublic | BindingFlags.Static, 2, 4); Assert.AreEqual(6, result); }
A little aberdabai is that invoking members that take an interface as argument, must be casted to the interfacetype – otherwise the PrivateType cannot identify the correct method to execute.
[TestMethod] public void PrivateStaticMemberWithInterfaceParameterTestSuccess() { var pt = new PrivateType(typeof(Adder)); IEnumerable param = new[] {new Number {Value = 2}, new Number {Value = 4}}; var result = (int)pt.InvokeStatic("AddNumbers", BindingFlags.NonPublic | BindingFlags.Static, param); Assert.AreEqual(6, result); }
Obviously, these classes just wraps some reflection magic underneath the covers, but they still come ind handy when testing legacy code. I would never advise to use these classes when doing green-field development. I strongly believe that it is a code-smell if it’s necessary to use these objects for testing new code.
MSDN; PrivateType
MSDN: PrivateObject