Question

I'm building a class library that includes several custom attributes that apply to properties. Then I have methods that do certain things based on the placement of the attributes.

Now I want to build some unit tests, but how to I make the tests without creating something on the order of x^(number of attributes) classes just for testing purposes? Can I leverage metadata classes or something?

Basically I'd love for there to be a way for me to apply attributes to properties at runtime (i.e. inside the "Arrange" part of my test method), but I'm pretty sure that's impossible.

Edit

This is the reflection code I'm using to test attributes, since apparently how I'm reading them may make a difference:

            bool skip = false, iip = false;
            string lt = null;
            SerializeAsOptions sa = SerializeAsOptions.Ids;

            object[] attrs = prop.GetCustomAttributes(true);
            foreach (object attr in attrs)
            {
                Type attrType = attr.GetType();
                if (typeof(JsonIgnoreAttribute).IsAssignableFrom(attrType)) 
                {
                    skip = true;
                    continue;
                }
                if (typeof(IncludeInPayload).IsAssignableFrom(attrType))
                    iip = ((IncludeInPayload)attr).Include;
                if (typeof(SerializeAs).IsAssignableFrom(attrType))
                    sa = ((SerializeAs)attr).How;
                if (typeof(LinkTemplate).IsAssignableFrom(attrType))
                    lt = ((LinkTemplate)attr).LinkTemplate;
            }
            if (skip) continue;
Was it helpful?

Solution

I'm adding another answer, because since you now provided some code, the old one is too broad. It's now (mostly) obvious that:

  • you control the attribute-reading code
  • you are reading the code via reflection (PropertyInfo.GetCustomAttributes)

So. Since you are using Reflection, TypeDescriptors will not help. You'd need to:

  • either read the attrs differently so TypeDescr can be used
  • dynamically generate assemblies at runtime to generate classes with properties on the fly during tests

It can be very interesting/entertaining, but it can also turn into nice amount of work. But, since you control both sides of the code, none of these two is actually needed.

First, let's trim the code to significant parts:

somemethod(PropertyInfo prop)
{
    // ...
    object[] attrs = prop.GetCustomAttributes(true); // read ATTRs from PROP
    foreach (object attr in attrs) // scan the PROP's ATTRs
    {
        // check attr type, do something
    }
    // ...
}

The core of your problem is not:

adding/removing attributes during Arrange/Teardown part

but

forcing the loop over PROP's ATTRs to see attributes that your test specifies

Looking at the problem like this, the answer is almost obvious: your loop has to abstract from the "Read attributes" part.

object[] attributeReader(PropertyInfo prop)
{
    return prop.GetCustomAttributes(true);
}

somemethod(PropertyInfo prop)
{
    // ...
    object[] attrs = attributeReader(prop); // read ATTRs from PROP
    foreach (object attr in attrs) // scan the PROP's ATTRs
    {
        // check attr type, do something
    }
    // ...
}

Now, your processing code is independent of the way the attributes are read. Sure, in the example above that way is hardcoded. But it does not have to be. Depending on how you want/like to organize your tests, you can use many ways to replace the attributeReader method with other mechanisms.

For example, just add 'virtual' to the attributeReader and use inheritance to create a class that will enable AttributeFaking:

// original class:
virtual object[] attributeReader(PropertyInfo prop)
{
    return prop.GetCustomAttributes(true);
}

// derived class:
object[] AttributesOverrides {get;set;}
override object[] attributeReader(PropertyInfo prop)
{
    if(prop.Name = "ShoeSize") return AttributesOverrides; // return what I say!
    return base.attributeReader(prop);
}

// your test setup
var t = ... // that DERIVED object
t.AttributesOverrides = new [] { ... } ; // attributes to use

For example, use delegates/lambdas, no inheritace

// note the attributeReader is now a field, not function
Func<PropertyInfo, object[]> attributeReader = defaultAttributeReader;

static object[] defaultAttributeReader(PropertyInfo prop)
{
    return prop.GetCustomAttributes(true);
}


// and your test setup
var t = ... // that ORIGNAL object
t.attributeReader = customReaderForTheTest; // change the reader on the fly

// that's the reader-function to use in THIS TEST setup
static object[] customReaderForTheTest(PropertyInfo prop)
{
    if(prop.Name = "ShoeSize") return null; // crash when I say so! muhaHAHAhaa!
    return prop.GetCustomAttributes(true);
}

Both of those two examples end up with one class that is enables faking the attributes in some way, but that's not the only ways to do that. You can use IoC to inject the correct attributeReader. You can do that in any way you like - you just need to abstract from reading part and leave it 'open'.

OTHER TIPS

It is not possible to really apply the attribute at runtime to an existing class, but there are at least two ways you could do something similar to it - it depends on how exactly are you reading those attributes later.

The options focus on the 'really' and 'existing class' part:
1) don't do that, just fake adding them
2) apply them on a class that does not exist yet! ;)

First option is a CustomTypeDescriptor. In its implementations, you will be able to dynamically answer to any queries about Attributes for some class that uses it (-> see virtual GetAttributes method).

This leads to first way:

  • Create AttributableTestObject that i.e. inherits from your ClassCompatibleWithThatAttribute etc
  • Create something like DynamicallyAttributedClass : CustomTypeProvider that exposes a static property similar to IEnumerable<Attribute>
  • override the GetAttributes and return whatever was provided by that static property
  • on your AttributableTestObject class set a TypeDecriptorProvider attribute pointing to provider (you've got to implement it, again) that returns DynamicallyAttributedClass

Now, using that static property you can change what the GetAttributes returns, and therefore you can dynamically change the setof attributes that are visible through typedescriptor.

And here's the catch: Not all engines/observers/readers/(..) actually care about TypeDescriptors. Some simply read the metadata right away from the Reflection. Reflection will not check the typedescriptors. It will simply return an information that the AttributableTestObject class has a TypeDecriptorProvider property. But whe nusing used the ComponentModel mechanisms, the custom list of attribues will be visible.

That reminds me that the reading mechanisms simply sit at TypeDescriptor class, see its methods. There's also AddAttribute, so maybe you can even get what you want without actually implementing the stuff I said above - Try using AddAttribute and then GetAttribute on your target Type and check the results. It may "just work". But again, it will not fool the reflection.

So, there's a second, "more hardcore" approach - dynamic classes. With System.Reflection.Emit you can dynamically generate an assembly that will contain whatever IL code you wish, and then load the assembly to the current runtime.

This gives you a unique option to simply generate a new fresh class(es) at runtime, with any contents and any attributes you like. You can use inheritance, so you can inherit from any ClassCompatibleWithThatAttributes. It's not that easy to do manually, but there are some libraries that make using the IL emitter easier.

Note that the generated types will be generated at runtime. This means that at compile-time you will not have them checked, you must generate them perfectly or face some really rarely seen Exceptions. Also, note that as the types are not known at compile-time, you cannot simply new the objects. You will have to create the objects basing on their freshly-generated Type through i.e. Activator.CreateInstance(Type).

Also, even though you can generate as much new classes as you want - if you overdo it, you probably will eventually hit some CLR limit, or at leat OutOfMemory, since the generated and loaded assemblies actually occupy space. You can overcome it with AppDomains - you can create some extra AppDomains and generate and load the assemblies inside them, and then, finally, you can unload that extra domain to release all memory and also unload any assemblies that were loaded there. But, I suppose you will not generate that much types to really need that. I'm writing about it just-in-case. Dont worry until you hit the OutOfMemory or similar.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top