Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get unit test method attributes at runtime from within an NUnit test run?

I store various information about a given test (IDs for multiple bug tracking systems) in an attribute like so:

[TestCaseVersion("001","B-8345","X543")]
public void TestSomethingOrOther()

In order to fetch this information during the course of a test, I wrote the below code:

     public string GetTestID()
     {
        StackTrace st = new StackTrace(1);
        StackFrame sf;
        for (int i = 1; i <= st.FrameCount; i++)
        {
            sf = st.GetFrame(i);
            if (null == sf) continue;
            MethodBase method = sf.GetMethod();
            if (method.GetCustomAttributes(typeof(TestAttribute), true).Length == 1)
            {
                if (method.GetCustomAttributes(typeof(TestCaseVersion), true).Length == 1)
                {
                    TestCaseVersion tcv =
                        sf.GetMethod().GetCustomAttributes(typeof(TestCaseVersion), true).OfType<TestCaseVersion>()
                            .First();
                    return tcv.TestID;
                }
            }
        }

The problem is that when running tests through NUnit in Release mode, the method which should have the test name and these attributes is replaced by the following:

System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at NUnit.Core.Reflect.InvokeMethod(MethodInfo method, Object fixture, Object[] args)

UPDATE For anyone who is interested, I wound up implementing the code in the following way (so that any of the attribute values could be accessed, without changing any of the existing code that uses TestCaseVersion attribute:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = false)]
public class TestCaseVersion : PropertyAttribute
{
   public TestCaseVersion(string testCaseCode, string story, string task, string description)
   {
      base.Properties.Add("TestId", testCaseCode);
      base.Properties.Add("Description", description);
      base.Properties.Add("StoryId", story);
      base.Properties.Add("TaskId", task);
    }
}

public string GetTestID()
{
   return TestContext.CurrentContext.Test.Properties["TestId"];
}
like image 809
breed052 Avatar asked Aug 01 '12 15:08

breed052


1 Answers

If you are OK with having a single-valued test case version string (i.e. "001, B-8345, X543" instead of "001","B-8345","X543"), you should be able to make use of the TestContext functionality available in NUnit 2.5.7 and higher.

Specifically, you could define and use a test context Property attribute TestCaseVersion like this:

[Test, Property("TestCaseVersion", "001, B-8345, X543")]
public void TestContextPropertyTest()
{
    Console.WriteLine(TestContext.CurrentContext.Test.Properties["TestCaseVersion"]);
}

UPDATE BTW, If you do want to use a multi-valued representation of the test case version, you could define multiple properties, like this:

[Test, Property("MajorVersion", "001"), 
 Property("MinorVersion", "B-8345"), Property("Build", "X543")]
public void TestContextPropertyTest()
{
    Console.WriteLine(TestContext.CurrentContext.Test.Properties["MajorVersion"]);
    Console.WriteLine(TestContext.CurrentContext.Test.Properties["MinorVersion"]);
    Console.WriteLine(TestContext.CurrentContext.Test.Properties["Build"]);
}
like image 142
Anders Gustafsson Avatar answered Oct 27 '22 05:10

Anders Gustafsson