How to dynamically add attributes to a class with Castle.Core

Yesterday I was working on some unit tests that ensured that some user derived classes passed to a method were decorated with a specific attribute.

Since the mocking framework I used (Moq) did not support dynamically adding of attributes to a mocked type, my first approach was simply to create some private test classes that contained different permutations of the specified attribute, like this:

[TheAttribute(5)]
public class FakeClassWithAnAttributeSetToANumberLessThanTen
{
}
  
[TheAttribute(15)]
public class FakeClassWithAnAttributeSetToANumberGreaterThanTen
{
}
  
[TheAttribute(10, DefaultStuffz=Stuffz.Random)]
public class FakeClassWithAnAttributeSetWithDefaultStuffz
{
}

Alas, as the amount of tests grew, I realized that this wasn't a viable solution.

Finally I found a quite nice solution by using Castle.DynamicProxy available in the Castle project. I have personally never used the Castle framework before, but the experience using it was quite nice.

public class MyClass
{
}
  
[AttributeUsage(AttributeTargets.Class)]
public class MyClassAttribute : Attribute
{
}

To create an instance of MyClass, dynamically decorated with a MyClassAttribute you simply do as below.

public static MyClass CreateClassDecoratedWithAttribute()
{
    // Get the attribute constructor.
    Type[] ctorTypes = Type.EmptyTypes;
    var ctor = typeof(MyClassAttribute).GetConstructor(ctorTypes);
    Debug.Assert(ctor != null, "Could not get constructor for attribute.");
  
    // Create an attribute builder.
    object[] attributeArguments = new object[] { };
    var builder = new System.Reflection.Emit.CustomAttributeBuilder(ctor, attributeArguments);
  
    // Create the proxy generation options.
    // This is how we tell Castle.DynamicProxy how to create the attribute.
    var proxyOptions = new Castle.DynamicProxy.ProxyGenerationOptions();
    proxyOptions.AdditionalAttributes.Add(builder);
  
    // Create the proxy generator.
    var proxyGenerator = new Castle.DynamicProxy.ProxyGenerator();
  
    // Create the class proxy.
    var classArguments = new object[] { };
    return (MyClass)proxyGenerator.CreateClassProxy(typeof(MyClass), proxyOptions, classArguments);
}

And now you can test that this actually works.

[Fact]
public void Created_Class_Should_Be_Decorated_With_Attribute()
{
    // Given
    var myClass = CreateClassDecoratedWithAttribute();
  
    // When
    var attribute = myClass.GetType().GetCustomAttributes(typeof(MyClassAttribute), true)[0];
  
    // Then
    Assert.NotNull(attribute);
    Assert.IsType<MyClassAttribute>(attribute);
}