views:

255

answers:

2

I recently upgraded a C# project from .NET 3.5 to .NET 4. I have a method that extracts all MSTest test methods from a given list of MethodBase instances. Its body looks like this:

return null == methods || methods.Count() == 0
    ? null
    : from method in methods
      let testAttribute = Attribute.GetCustomAttribute(method,
          typeof(TestMethodAttribute))
      where null != testAttribute
      select method;

This worked in .NET 3.5, but since upgrading my projects to .NET 4, this code always returns an empty list, even when given a list of methods containing a method that is marked with [TestMethod]. Did something change with custom attributes in .NET 4?

Debugging, I found that the results of GetCustomAttributesData() on the test method gives a list of two CustomAttributeData which are described in Visual Studio 2010's 'Locals' window as:

  1. Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute("myDLL.dll")
  2. Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute() -- this is what I'm looking for

When I call GetType() on that second CustomAttributeData instance, however, I get {Name = "CustomAttributeData" FullName = "System.Reflection.CustomAttributeData"} System.Type {System.RuntimeType}. How can I get TestMethodAttribute out of the CustomAttributeData, so that I can extract test methods from a list of MethodBases?

+2  A: 

Have you tried using

method.GetCustomAttributes(typeof(TestMethodAttribute), false)

instead? Asking the target for the custom attributes has usually been the way I've gone about fetching them.

Here's a hasty example:

using System;
using System.Linq;

[AttributeUsage(AttributeTargets.All)]
public class FooAttribute : Attribute {}

class Test
{
    static void Main()
    {
        var query = typeof(Test).GetMethods()
            .Where(method => method.GetCustomAttributes(
                              typeof(FooAttribute), false).Length != 0);

        foreach (var method in query)
        {
            Console.WriteLine(method);
        }
    }

    [Foo]
    public static void MethodWithAttribute1() {}

    [Foo]
    public static void MethodWithAttribute2() {}

    public static void MethodWithoutAttribute() {}

}
Jon Skeet
@Jon: yes, and I've tried passing `true` so it checks ancestors, too. I always get back an empty array of `object`s.
Sarah Vessels
@Sarah: In that case, please post a short but complete program demonstrating the problem. I've shown an example which *does* work.
Jon Skeet
@Jon: false alarm! My project was referencing the old .NET 3.5/VS 2008 version of the UnitTestFramework library. Switching to the .NET 4/VS 2010 version of UnitTestFramework (10.0.0.0) fixed the problem.
Sarah Vessels
@Sarah: Ah, right - so the reflection attribute type wasn't the same as the one on the methods...
Jon Skeet
+2  A: 

Silly mistake on my part: my test-method-extracting method was in a Class Library project that referenced Microsoft.VisualStudio.QualityTools.UnitTestFramework so that it could look for TestMethodAttribute as a custom attribute. When I upgraded my Solution from VS 2008 to VS 2010, the conversion process automatically updated references from Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=9.0.0.0 to Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0 in my test projects. It did not update the reference in my Class Library project, however, so that was still pointing to the old UnitTestFramework reference. When I changed that project to point to the 10.0.0.0 library, my code below worked as expected:

return null == methods || methods.Count() == 0
    ? null
    : from method in methods
      let testAttribute = Attribute.GetCustomAttribute(method,
          typeof(TestMethodAttribute))
      where null != testAttribute
      select method;

Also, the code Jon suggested worked as well, once I updated the reference.

Sarah Vessels