Derik Whittaker

Syndication

News


Your unit test may smell if…….!

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
Filed under: , ,

[Advertisement]

Comments

NotMyself wrote re: Your unit test may smell if…….!
on 09-09-2009 5:03 PM

Hey Derik,

Instead of subclassing wouldn't it be simpler to create extension methods that are test assembly scoped to hide this stuff away?

Then you can have:

var instance = new SomeClass();

instance.SetLastVerifiedTime(val);

instead of:

var wtfamitesting = new MySubclassOfSomeClass();

wtfamitesting.SetLastVerifiedTime(val);

I think it makes the tests more intention revealing.

Mark Needham wrote re: Your unit test may smell if…….!
on 09-09-2009 5:54 PM

The only thing I don't like about the sub classing idea for testing is that we're now not testing what's in production and it is theoretically possible that we put some code in those sub classes which makes us think our code works when actually it doesn't.

We had one occasion where setting data by reflection seemed reasonable - if I remember correctly it was because we were loading read only objects out of a database via Hibernate and those objects were never initialised inside our code so we had no need to build them up by passing in values through a constructor.

I prefer the reflection approach than changing the access modifier of the fields but maybe the idea suggested above might work better for keeping that reflection logic out of the test.

Derik Whittaker wrote re: Your unit test may smell if…….!
on 09-09-2009 8:13 PM

@Mark,

I would not agree that adding the subclass in this case changes the code being tested.  Remember the subclass is pushing data into private fields and would then call a public method.

Sanjeev Agarwal wrote Daily tech links for .net and related technologies - September 9-12, 2009
on 09-10-2009 5:52 AM

HTML clipboard Daily tech links for .net and related technologies - September 9-12, 2009 Web Development

Graham wrote re: Your unit test may smell if…….!
on 09-10-2009 10:59 AM

"Tests are code too"... I like that, I may get a t-shirt made!

Code Monkey Labs wrote Weekly Web Nuggets #76
on 09-14-2009 11:31 AM

Pick of the week: How to Contribute to Open Source Without Writing a Single Line of Code General Microsoft Creates the CodePlex Foundation : Scott Hanselman announces the CodePlex Foundation , a non-profit organization created to help commercial software

Steve Py wrote re: Your unit test may smell if…….!
on 09-15-2009 12:07 AM

Typically I prefer to keep the scope of members private unless the code design benefits from inheritance. In those cases I typically have a public getter with protected setter so subclassing is an option.

This is one scenario that I use a preprocessor symbol.

#if TEST

public SomeClass(IDependency someDependency, ...)

{

 _someDependency = someDependency;

}

public void SetLastVerifiedDate(DateTime date)

{

 _lastVerifiedDate = date;

}

#endif

I utilize this approach to manage dependencies when using a property-injection IoC container, plus setting up private members. (Dependency setters aren't public either.) IMO code is code, whether test specific sub-classes or otherwise.

Jordan Day wrote re: Your unit test may smell if…….!
on 09-28-2009 12:16 PM

I'm just getting around to this post since I've been hammered at work the last few weeks (my RSS reader is going to take *days* to work through!) but your post intrigued me because I just realized I could "inject" test data into a protected/private field via reflection a few weeks ago (I''ll admit I'm still a pretty big newbie, especially when it comes to unit testing). We're using a framework that provides automatic validation of business rules when an object's properties are set, but we ended up creating a public prop and a protected prop because we didn't want validation occurring when retrieving stored data (this may be a "code smell", I don't really know). Some of the validation rules require a db connection (for example, "make sure no existing widgets have the same name as this new widget"). When trying to write unit tests I bumped up against this issue (that a db connection was required for for a particular validation rule to run), and we're not using a IoC container and there's not a non-trivial way to mock out the code that connects to the db inside the validation rule, so long story short when I realized I could use reflection to set a value on the protected version of the property for a stub, I felt really clever!

I will admit that re-writing a bunch of reflection code in several tests is tedious, but I don't know, creating a bunch of subclasses almost feels like "the nuclear option"  (I'm sure someone would argue that using reflection is in fact the "nuclear option").

Long story short, I *was* feeling really clever because I thought I had this neat hack to allow me to unit-test what I thought was otherwise un-unit-testable code, but now I just feel like a newbie again (subclassing hadn't even really occurred to me!) Anyway, keep the posts coming, I always enjoy learning new things.

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)