If you find yourself using reflection in your unit test to push 'stub’ data into it your test just may smell. Now there are times (especially when dealing with legacy code) that you need use reflection to crack open a class to push/pull values but I would strongly suggest you consider the solutions I am going to suggest.
Utilizing the wrap method (see Working with Legacy Code) to accomplish your goal.
Lets assume you have the following class which you want to test:
public class SomeClass
{
private DateTime _lastVerified;
private DateTime _lastActivated;
}
Now one way to push data into these fields would be to do the following:
[Test]
public void IsTimeToActivateTrueLastActivatedTheDayBeforeCurrentTimeAfterActivationTimeTest()
{
SomeClass accessor = new SomeClass
Type type = SomeClass
FieldInfo field2 = type.GetField("_lastActivated", BindingFlags.Instance | BindingFlags.NonPublic);
field2.SetValue(accessor, new DateTime(2008, 2, 29, 10, 5, 0));
FieldInfo field3 = type.GetField("_lastVerified", BindingFlags.Instance | BindingFlags.NonPublic);
field3.SetValue(accessor, new DateTime(2008, 3, 1, 10, 0, 0));
DateTime currentDateTime = new DateTime(2008, 3, 1, 10, 10, 0);
MethodInfo method = type.GetMethod("SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic);
object result = method.Invoke(accessor, new object[1] { currentDateTime });
bool isTime = (bool)result;
Assert.AreEqual(true, isTime);
Assert.AreEqual(currentDateTime, field3.GetValue(accessor));
}
Now this works, but if you do this over and over again you will kill yourself with reflection code.
Option 1) Create a subclass of your class and hide the reflection inside your subclass
public class MySubclassOfSomeClass : SomeClass
{
public void SetLastActivatedTime( DateTime value )
{
// set the value via reflection here
{
public void SetLastVerifiedTime( DateTime value )
{
// set the value via reflection here
{
}
The code above will still use reflection to set the value, but it will hide the usage of reflection from your unit test and will present a less noisy test to future developers
Option 2) Change the scope of the fields and subclass and extend
public class SomeClass
{
protected DateTime _lastVerified;
protected DateTime _lastActivated;
}
public class MySubclassOfSomeClass : SomeClass
{
public void SetLastActivatedTime( DateTime value )
{
_lastActivated = value;
{
public void SetLastVerifiedTime( DateTime value )
{
_lastVerified = value;
{
If you change the scope of the fields to protected you could subclass the original class and do the same as option 1, but because you have access to the fields via inheritance there is no need to use reflection.
So, remember that tests are code to and if you feel pain writing test you may be doing it wrong.
Till next time,
Posted
09-09-2009 12:28 PM
by
Derik Whittaker