Question

Suppose you have a .dll file containing NUnit tests, where all test procedures have the [Test] attribute. Some of the tests are documented, which means they are marked with [Test, Description(...)].

I thought I could, using reflection in PowerShell, retrieve all the functions that have the [Test] attribute and the relative description (if any).

I tried to see if I could see said attributes in the following:

$suite = [Reflection.Assembly]::ReflectionOnlyLoadFrom("D:\test_suite.dll")
[reflection.customattributedata]::GetCustomAttributes($suite)

But what I can see shows no trace of Test:

AttributeType                Constructor                 ConstructorArguments        NamedArguments             
-------------                -----------                 --------------------        --------------             
System.Runtime.InteropSer... Void .ctor(System.String)   {"02cfdc16-5480-4bb6-890... {}                         
System.Diagnostics.Debugg... Void .ctor(DebuggingModes)  {(System.Diagnostics.Deb... {}                         
System.Reflection.Assembl... Void .ctor(System.String)   {"Copyright © Microsoft ... {}                         
System.Reflection.Assembl... Void .ctor(System.String)   {"1.0.0.0"}                 {}                         
System.Runtime.InteropSer... Void .ctor(Boolean)         {(Boolean)False}            {}                         
System.Runtime.CompilerSe... Void .ctor(Int32)           {(Int32)8}                  {}                         
System.Runtime.CompilerSe... Void .ctor()                {}                          {WrapNonExceptionThrows ...
System.Reflection.Assembl... Void .ctor(System.String)   {""}                        {}                         
System.Reflection.Assembl... Void .ctor(System.String)   {"Test_Test_Suite"}         {}                         
System.Reflection.Assembl... Void .ctor(System.String)   {""}                        {}                         
System.Reflection.Assembl... Void .ctor(System.String)   {""}                        {}                         
System.Reflection.Assembl... Void .ctor(System.String)   {"Microsoft"}               {}                         
System.Reflection.Assembl... Void .ctor(System.String)   {"Test_Test_Suite"}         {}

I'm clearly going the wrong path. Any hint?

Was it helpful?

Solution

The problem is that you are looking for attributes on the assembly, not on the methods in the assembly.

To do that, just get the types in the assembly and then their methods and then filter those to get only the methods which have the TestAttribute and not the DescriptionAttribute.

So, to start with, just get the assembly metadata as you were doing already:

$testSuite = [System.Reflection.Assembly]::ReflectionOnlyLoadFrom("c:\testsuite.dll")

Then get the methods available in the assembly:

$methods = $testSuite.GetTypes().GetMethods()

When you have those, filter out the methods which have the TestAttribute. You could probably determine the existance of TestAttribute in a better way than this, for example giving the exact attribute name you are looking for or similar, but I didn't use the NUnit attributes in my own sample.

$testMethods = $methods | 
    Where { 
        $_.GetCustomAttributesData() | 
            Where { $_.AttributeType.FullName -like "*.TestAttribute" } 
    }

And when you have done so, just pick the data you want from this (I included the DeclaringType as well, just remove it if you don't want it in your selection):

$testDocumentation = $testMethods | 
    Select DeclaringType, Name, @{
        Name = "Description"
        Expression = { 
            $descriptionAttribute = $_.GetCustomAttributesData() |
                Where { $_.AttributeType.FullName -like "*.DescriptionAttribute"}
            Write-Output $descriptionAttribute.ConstructorArguments[0].Value
        }
    }

Or you could do it all in one line, if you are so inclined:

[Reflection.Assembly]::ReflectionOnlyLoadFrom("$pwd\ClassLibrary2.dll").GetTypes().GetMethods() | 
    Where { $_.GetCustomAttributesData() | Where { $_.AttributeType.FullName -like "*.TestAttribute" } } | 
    Select DeclaringType, Name, @{
        Name = "Description"
        Expression = { 
            $descriptionAttribute = $_.GetCustomAttributesData() |
                Where { $_.AttributeType.FullName -like "*.DescriptionAttribute"}
            Write-Output $descriptionAttribute.ConstructorArguments[0].Value
        }
    } 

With my little test assembly, this resulted in the following output:

DeclaringType                 Name                         Description                 
-------------                 ----                         -----------                 
TestSuite.TestClass1          FirstTest                    This is the first test
TestSuite.TestClass1          SecondTest                   This is my second test
TestSuite.TestClass1          ThirdTest                    This is my third test
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top