Recently I was reading a blog post dealing with accessing private and internal members in tests. Visual Studio provides the PrivateObject class, that can be used to access private and internal members of a class.
Beside the fact that accessing private members in a test should generally be avoided, there are also situations where it may indeed be helpful.
Unfortunately, using PrivateObject in a test creates “noise” that makes the test harder to read. So let’s assume we want to test this class and access its _someText field:
namespace MyCupOf.Net { internal class MyInternalClass { private string _someText; } }
Using PrivateObject in a test method to access the private field _someText would look like this:
PrivateObject sut = new PrivateObject("MyCupOf.Net", "MyCupOf.Net.MyInternalClass"); string expected = "some text"; sut.SetField("_someText", expected); string actual = (string)sut.GetField("_someText"); Assert.AreEqual(expected, actual);
This works as promised, but the code does not look very nice.
Fortunately, by using the C# dynamic keyword and implementing a DynamicObject we can greatly improve the readability of the test. First let’s look at the rather simple implementation of DynamicPrivateObject:
class DynamicPrivateObject : DynamicObject { private readonly PrivateObject _po; public DynamicPrivateObject(string assemblyName, string typeName) { _po = new PrivateObject(assemblyName, typeName); } public override bool TrySetMember(SetMemberBinder binder, object value) { _po.SetFieldOrProperty(binder.Name, value); return true; } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = _po.GetFieldOrProperty(binder.Name); return true; } public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { result = _po.Invoke(binder.Name, args); return true; } }
Armed with this class, we can simplify the test method:
dynamic sut = new DynamicPrivateObject("MyCupOf.Net", "MyCupOf.Net.MyInternalClass"); string expected = "some text"; sut._someText = expected; string actual = sut._someText; Assert.AreEqual(expected, actual);
This looks like completely naturally assiging and reading a field value. The “noise” is under the hoods.
Hope you like it.
Hi Florian,
danke. Deine Version ist noch ein wenig umfangreicher, als die Variante von manicai.tumblr.com …
=> Yes, I like it. 😉
Ciao,
Mike