Derik Whittaker

Syndication

News


Using Reflection to enforce your conventions

I know that reflection is nothing new, in fact many people may even call it old hat.  However, it does still have its place and can still be very, very useful.  The other day I wanted to write a set of tests which would ensure that out developers put the [Serializable] attribute on all our service request/response objects.  We must have this attribute on these objects because we are dealing with a remoting boundary.  The reason I wanted to add this test is because many times when we create objects which must cross our boundary we forget to put the [Serializable] attribute on our class and we have to wait until runtime to learn our fate.  What makes this test easy for us is that we have a standard convention which will allow for us to search for and find all our Response and Request objects (yea conventions)

The good news for us is that Reflection makes writing this test very easy and very straight forward.

Before we start to dive into the code we should take a look at a ‘standard’ response class

[Serializable]
public class DoSomethingResponse
{
	// body goes here
}

Now that we have seen an example Response object (I know, I know it was a lame example but deal with me here) we need to take a look at the logic which allows us to find our attributes via reflection.

The code below is NOT the unit test code, but it is the reflection guts which will be called by my unit tests

private StringBuilder SearchForTypes( Assembly assembly, string searchSuffix )
{
    var stringBuilder = new StringBuilder();

    // #1
    foreach ( var type in assembly.GetTypes() )
    {
        bool foundSerializableAttribute = false;

        // #2
        if ( type.Name.EndsWith( searchSuffix ) )
        {

            // #3
            var customAttributes = type.GetCustomAttributes( typeof( TYPE ), true );

	    // #4
            if ( customAttributes == null )
            {
                stringBuilder.AppendLine( string.Format( "Type {0} is missing the serializable attribute", type.Name ) );
            }
        }
    }

    return stringBuilder;
}

Looking at the code above there are a few items I should point out

  1. This will loop through all the types for the given loaded assembly.  This will look for both public and private types
  2. When find a type, we want to check its name to see if the naming on the class matches the naming convention we are looking for (DoSomethingResponse is a Response convention)
  3. We want to pull All custom attributes for the given type (System.Serializable) off the class
  4. We want to make sure there was an attribute found, if there was NOT then we want to log that for later.

The code below is the unit test code which will call the code above.

[Test]
public void Response_EnsureAllResponseClassesAreSerializable()
{
    
    var assembly = Assembly.GetAssembly( typeof( IRemotingServiceMarker ) );

    var stringBuilder = SearchForTypes( assembly, "Response" );

    Assert.That( stringBuilder.Length == 0, stringBuilder.ToString() );
}

The test above is pretty straight forward, we are loading the assembly based on a type in the assembly then we simply call off to our method we defined above.  Because we do not want to simply fail on the first missing class we will return a list of any classes which should contain our marker, but do not.

As you can see, using reflection to find markers on a class is pretty easy. 

Till next time,


Posted 08-04-2009 7:22 AM by Derik Whittaker
Filed under: ,

[Advertisement]

Comments

Scott Seely wrote re: Using Reflection to enforce your conventions
on 08-04-2009 10:20 AM

This seems like a use case that FxCop would fill pretty well. However, this also has me wondering if there is value in porting FxCop's tests to MbUnit and other testing frameworks.

Why didn't you choose FxCop?

Zdeslav Vojkovic wrote re: Using Reflection to enforce your conventions
on 08-04-2009 10:26 AM

Using IsDefined() instead of GetCustomAttributes() usually performs a bit better (which is always good for unit tests)

Derik Whittaker wrote re: Using Reflection to enforce your conventions
on 08-04-2009 10:28 AM

FXCop... did not think of that.

Randall Sutton wrote re: Using Reflection to enforce your conventions
on 08-04-2009 12:16 PM

Excellent post.  I often wonder if reflection is under utilized due to it's performance stigma.

Matt Kelly wrote re: Using Reflection to enforce your conventions
on 08-04-2009 12:30 PM

Good idea.

Possible enhancements:

* return the types that didn't fulfill the requirement and format your message in your test, making the method more reusable

* similarly pass in a Func<Type, bool> to determine candidate Types rather than the string suffix, meaning you could extract types using more complex rules

Oh and you don't end up using foundSerializableAttribute ;)

Tuna Toksoz wrote re: Using Reflection to enforce your conventions
on 08-04-2009 6:57 PM

What if one of its fields is not serializable? don't you need to consider that too?

Derik Whittaker wrote re: Using Reflection to enforce your conventions
on 08-04-2009 7:02 PM

@Tuna,

Good idea, I had not thought of that yet.  Maybe something that walks the tree of each objects and checking all public points/classes to make sure they are also serializable.

Mike wrote re: Using Reflection to enforce your conventions
on 08-05-2009 5:31 AM

This is a good technique for enforcing these types of conventions I do something similar (code.google.com/.../conventions.cs) to make sure all my controllers have their public methods set to virtual so they get unhandled exception logging out of the box from a windsor custom Inteceptor, less infrastructure code yay! :-)

Rjae Easton wrote re: Using Reflection to enforce your conventions
on 08-05-2009 8:27 AM

In Pre-NUnit 2.5.x do the following:

using (MemoryStream stream = new MemoryStream())

   new BinaryFormatter().Serialize(stream, new DoSomethingResponse());

In 2.5.x do the following:

Assert.That(new DoSomethingResponse(), new BinarySerializableConstraint());

Very nice post.

MotoWilliams wrote re: Using Reflection to enforce your conventions
on 08-06-2009 2:05 AM

I do a similar thing for unit test projects.  Making a CSV from the Namespace, Classname and TestMethod.  Bolt on a Excel pivot table and you have a nice layout of your tests.

nappisite wrote re: Using Reflection to enforce your conventions
on 08-06-2009 9:08 AM

I have an alternative solution to this problem,  I wrote a utility to serialize and deserialize an object, which essentially verifies that the object (and its whole object graph of child objects) can all be serialized.  

However, it lacks the recursion of inspecting an entire assembly, and requires the the object being tested has been instantiated.

Ray O'Neill wrote re: Using Reflection to enforce your conventions
on 08-06-2009 9:14 PM

I've used a similar trick where I have an integration test that inspects our Linq-To-Sql mappings (not using attributes) via reflecting on our DataContext-class and calling FirstOrDefault() which is a reasonable way to assert that the table and column/property mapping is valid against the database schema. It's saved me a couple times already!

Igor Brejc wrote re: Using Reflection to enforce your conventions
on 08-07-2009 7:57 AM

Isn't there some kind of a simple to use fluent interface library to work with Reflection? Something like Windsor's registration API?

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)