Translating Unit Test Names Into Requirements With .NET Attributes

A good stance and posture reflect a proper state of mind.”
– Morehei Ueshiba

The application that I work on for my current employer is, for all intents and purposes, a legacy application with very little unit testing done on it. When I came onto the project, the majority of the functionality had been completed and it was my job, along with my colleagues, to shore it up and bring it to market. For that version of the software we relied heavily on the functional test plans defined by the project manager and QA. The software worked remarkably well given that our predecessors had only high level requirements to work with and did absolutely no unit testing.

Since that release, we have finished 3 maintenance releases and are now working on the first minor release. Over the interim we have added quite a few unit tests. Many of these were written as safety nets in order to ensure that functionality did not change as we fixed bugs, re-factored, and added new features. The practice of adding unit tests to cover current functionality and ensure we don’t break things as we move forward has had the side effect of revealing many of the lower level business requirements of the software. It became apparent that it would be useful to capture these requirements in the documentation of the software before the next release, so I was tasked with making a tool that would accomplish this task.

The unit test naming convention we use for all of our tests is a flavor of the Given-When-Then naming convention. This made it trivial to construct a small console application that would reflect on the test classes in the test libraries for our application and output a list of requirements. It did present 2 difficulties however:

  1. Many of the unit tests cover very technical requirements that shouldn’t be represented in the list of business requirements.
    (i.e. When_an_arg_is_null_then_an_argument_null_exception_is_thrown)
  2. Some of the test names were intentionally left ambiguous in order to avoid having to rename the test methods every time a requirement on some label changed.
    (i.e. When_some_string_label_is_used_then_it_meets_requirements)

Because I was already planning on using reflection to produce my list of requirements, .NET attributes were the prime candidate for resolving both the issues. Figuring out which tests define business requirements versus technical requirements is as easy as decorating each with a [TestCategory("Business Requirement")] or a [TestCategory("Technical Requirement")] attribute, respectively. Then the application can reflect on the [TestCategory()] attribute an behave appropriately based on the values in the TestCategories list.

In order to specify the value of the property that the unit tests are referring to when they are titled “[…] then_it_meets_requirements”, I created a custom method attribute like so:

    [AttributeUsage(AttributeTargets.Method)]
    public class RequirementValueAttribute : Attribute
    {
        private readonly object _requirementValue;

        public RequirementValueAttribute(object requirementValue)
        {
            _requirementValue = requirementValue;
        }

        public string GetRequirementValueAsString()
        {
            return _requirementValue as string;
        }

        public int GetRequirementValueAsInteger()
        {
            return (int)_requirementValue;
        }

        /.../
    }

This attribute can be used to decorate any test method that states there is a requirement and the backing field in the attribute can carry the specified value. For example, if the label for name in my application was required to be “Nombre”, then the attribute decorating the test for the name label would be [RequirementValue("Nombre")]. Likewise, if the requirement was that the value be integer 3, then the attribute decorating the test for the integer value would be [RequirementValue(3)].

Using this method to tell what the required value is has a nifty side effect. Reflection can be used inside the test method to retrieve the requirement from a common location which makes updating requirements in the unit tests super easy. The result would look something like this:

     [TestMethod]
     [RequirementValue("Nombre")]
     public void Then_the_name_label_meets_requirements()
     {
       var method = MethodBase.GetCurrentMethod();
       var attribute = (RequirementValueAttribute)method.GetCustomAttributes(typeof(RequirementValueAttribute), true)[0];
 
       var expectedNameLabel = attribute.GetRequirementValue();
       var actualNameLabel = MyClass.MyNameLabel.ToString();
 
       Assert.AreEqual(expectedNameLabel, actualNameLabel);
     }

Or, some other type of reflection could be used so that all the logic needed to extract the requirement value could be encapsulated; like my colleague suggested when he did this:

      public static class RequirementHelper
      {
        public static string GetRequirementValue()
        {
          var stackTrace = new StackTrace();
          var caller = stackTrace.GetFrame(1).GetMethod();

          var attribute =(RequirementValueAttribute)caller.GetCustomAttributes(typeof(RequirementValueAttribute), true)[0];

          return attribute.GetRequirementValueAsString();
        }
      }

This makes the previous example look like this:

     [TestMethod]
     [RequirementValue("Nombre")]
     public void Then_the_name_label_meets_requirements()
     {
       var actualNameLabel = MyClass.MyNameLabel.ToString();
 
       Assert.AreEqual(RequirementHelper.GetRequirementValue(), actualNameLabel);
     }
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s