Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

ClassTester, or "How to exercise your assembly"

Fear not, brethren and…uhhh….sistren. The resistance is still alive. But the revolutionary jargon will get in the way here so I’m reverting to a more traditional means of expression.

Among the things we’ve implemented as part of our project clean-up is the aptly-named ClassTester. It’s a neat little tool that takes some of the pain out of routine testing of constructors and properties by, for example, setting a property then checking to make sure you get the same value back from the getter. Here’s some sample syntax for testing the properties of an object:

    ClassTester tester = new ClassTester(new myObject());
tester.TestProperties();

Now, hillbillies are not what you might call amenable to writing boilerplate code for every single class in their domain. Leads to some problems you can only imagine in your nightmares. So in an attempt to avoid duplication, I decided instead to walk through all the classes in the assembly:

       [Test]
1       public void Test_general_constructors_and_properties()
2       {
3           Assembly assembly = Assembly.GetAssembly(typeof (Still));
4           Type[] types = assembly.GetTypes();
5           foreach (Type type in types)
6           {
7               if ( type.IsClass 
8                    && !type.IsAbstract 
9                    && type.Namespace == "Suvius.Applications.HoochDeriver.Domain" ))
10              {
11                  ClassTester.TestConstructors(type, false);
12                  object item = assembly.CreateInstance(type.FullName);
13                  ClassTester tester = new ClassTester(item);
14
15                  tester.TestProperties();
16              }
17          }
18      }

Some points of interest:

  • I could use any domain object in line 3. I’m interested only in getting the assembly that houses all the domain objects
  • Line 8: The ClassTester doesn’t much like abstract classes
  • Line 9: This line may seem redundant but it is necessary because of the way I run my builds (which, credit where it’s due, I pilfered from JP Boodhoo). Namely, in the build file, we compile all .cs files into a single assembly and run the tests against it. Which means line 3, will retrieve the assembly containing (almost) every class in the application. Compare this with compiling in the IDE where all domain classes are in one assembly by themselves. So running this test from the IDE, where it will compile the assemblies separately, we don’t need the check for the namespace. Running it in the build file however, we do.

There are in fact two separate tests here, one for constructors and one for properties. If you want to argue that these should be separate tests, I’ll agree. If you want to argue against that, I’ll agree too. That’s how much I care about it.

Anyway, this code is nice in theory. At present, the tester handles only very routine cases. If there are any validation rules, you’ll get validation errors. For example, if you have a Percent property where you throw an exception if the value is not ‘twixt 0 and 100, the ClassTester will throw this exception most of the time because it generates a random number to set the properties and doesn’t know about your little rules. The ClassTester does allow you to ignore individual properties though, which is useful when testing classes individually.

The second problem with looping through every class is that not every class can be tested, even with no validation rules. In our case, we had some classes that were inheriting from CollectionBase (did I mention it was ported over from .NET 1.1?) and the ClassTester failed on all of these because of the Capacity property (which I didn’t know existed).

The end result is that we had to include a list of classes to ignore from the domain and make sure they were tested separately through more conventional methods. Whether or not this is worth it depends on how many classes you need to ignore. In our case, it was only the collection classes which have since been removed anyway because of the generic collections in .NET 2.0.

And to prove it was all worthwhile, the ClassTester found and located no less than three bugs in our getters and setters, each one a variation on this theme:

    public decimal AmountOfHopsToAdd
    {
        get
        {
            if (DandelionBase != null)
            {
                // Return some complicated formulaic value based on DandelionBase
            }
            else return 0.0M;
        }
        set { _amountOfHopsToAdd = value; }
    }

In this specific case, the setter is all but useless since _amountOfHopsToAdd is never actually used anywhere. The other examples gave similar fodder for pondering and our code is shinier and happier for it.

Kyle the Testable

This entry was posted in Refactoring, Utilities. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://kyle.baley.org Kyle Baley

    Andy,

    We’ve set it up so that the deployment process uses a different .gwt.xml file for each environment. If you can do this, it’s not an issue. It’s not something we even think about anymore.

    Capybara also allows selecting by CSS selector. In practice, this ends up being similar to XPath but less verbose. E.g.

    page.find(‘label’, :text => “My Label”)

    XPath will probably be the way to go with complicated things like finding based on sibling relationships and such but Capybara does help along the way.

  • Andy Bari

    Thanks for the Information, I am new to this. And using ID is so much quicker. But the problem is our software goes to UAT pretty quick so we cant turn on and off the feature you mentioned. So do I persume using xpath is the best way?

  • http://codebetter.com/blogs/kyle.baley Kyle Baley

    I believe the actual quote was: “Don’t you people have an off button? AND WHY IS IT SO &*^% COLD?!?!”

  • http://www.shaneo.ca Shaneo

    I seem to remember a question in an email coming from you at DevTeach saying something to the effect of “Don’t you people ever turn off?”

    I’ll pose the same question back to you as you’ve been posting fast and furious!

    On a level of Seriousness though – Great posts, keep them coming. I enjoy reading :)