There are two ways you might use ActiveSharp as a unit test tool.
  • The first is simply as a low-impact solution to the property changed problem.
  • The second, is as a more general-purpose tool for inspecting MSIL at runtime. In the latter role, it becomes like a (very) light-weight FxCop or similar, fully integrated into your unit test suite. See second half of page, below, for details.

Low-Impact Solution to the Property Changed Problem

The standard example of using ActiveSharp looks like this:

    public int Foo
    {
        get { return _foo; }
        set { SetValue(ref _foo, value); }  // <-- no property name here
    }

But what if you don't want ActiveSharp's funky MSIL inspection and by-ref-field-identification in your application(*)? Well, good news, you can use ActiveSharp as a unit test tool instead. That way, it won't be used at runtime at all.

To do so, you need to return to writing your property setter methods with hard-coded string names for the properties. Something like this:

    public int Foo
    {
        get { return _foo; }
        set { SetValue(ref _foo, value, "Foo"); }  // this time, we DO have a hard-coded property name
                                                   // Here, SetValue is assumed to be your own routine, not part of ActiveSharp.
    }

Then, you can use ActiveSharp at unit test time, to assert that each property includes its own name, as a string literal. So, if you rename the property, but forget to change the string, your unit test will fail. Here's what your test might look like:

[Test]
public void PropertiesWithChangeNotificationMustContainTheirOwnNamesAsStrings()
{
    IEnumerable<PropertyInfo> propertiesToTest = ... // get the list of properties you want to test, here

    var badProperties = from p in propertiesToTest
                        let getter = p.GetSetMethod()          // only looking at the setter, not the getter, in this example
                        let inspector = new ActiveSharp.Inspection.MethodInspector(getter, false)
                        let containsOwnNameAsStringLiteral = inspector.ContainsStringLiteral(p.Name)
                        where !containsOwnNameAsStringLiteral
                        select p;

    var firstBadProperty = badProperties.FirstOrDefault();
    if (firstBadProperty != null)
    {
        // For simplicity, we are only reporting the details of the first failure in this example
        // You may prefer to report them all
        Assert.Fail("Property does not contain its own name as a string literal.  Property: " + 
                    firstBadProperty.DeclaringType.FullName + "." + firstBadProperty.Name);
    }
}

Note that the way you obtain the list of properties, to check, will depend on your situation. You can do it by normal reflection, no ActiveSharp required at that point, or you can enhance that approach slightly, as follows: if the properties you wish to check all contain calls to a particular method (e.g. your own "SetValue" method), then you can obtain the list of all properties by reflection, and then use ActiveSharp's ContainsCallTo (below) to filter them down to only those properties that call SetValue.

Please post any questions below, or on the Discussions tab.

(*) In the 2 years since its first release, no-one has reported any problems with ActiveSharp's funky approach. But, considering it dynamically generates code which cannot even be written in any (one) .NET language, you may choose to err on the side of caution.

Inspecting MSIL at Runtime (lightweight alternative to FxCop)

ActiveSharp includes a relatively simple MSIL parser. While this parser is not enough for detailed analysis of data flows within methods (I haven't written the support for that yet) it is enough to make a few basic checks. Here are the methods and properties you can call, in the form of some sample code:

        System.Reflection.MethodInfo methodToLookAt = ....; // obtain this via normal reflection, e.g. Type.GetMethod

        ActiveSharp.Inspection.MethodInspector ins = new MethodInspector(methodToLookAt, false);

        // what are all the methods that it (directly) calls?
        IEnumerable<MethodBase> callees = ins.AllCalledMethods;

        // does it read the value of DateTime.Now?
        bool callsDateTimeNow = ins.ContainsCallTo(typeof(DateTime).GetProperty("Now").GetGetMethod());

        // what are all the string literals that it contains?
        IEnumerable<string> stringLiterals = ins.AllStringLiterals;

        // does it contain the string literal "Foo"
        string s = "Foo";
        bool containsFoo = ins.ContainsStringLiteral(s);

        // ADVANCED
        // Give me a list of all its MSIL instructions, so I can do some further processing/analysis
        IEnumerable<ActiveSharp.Inspection.Instruction> instructions = ins.Instructions;

You can use these methods for a number of situations where you want to make assertions about the "insides" of methods.(*)

For example, on one project of mine, we had a rule that no class could call DateTime.Now. Instead, whenever a class needed the current time, it should call a method on our own "Clock" class. (The reason for this was that the Clock class could lie, for testing purposes. This was very handy for doing unit and functional testing of long-running tasks). I wrote a unit test which used reflection to get the MethodBase object for every single method in the system. (All methods, all constructors, plus all property setters and getters). Then, I instantiated an ActiveSharp MethodInspector for each, and used its ContainsCallTo method, to see whether it contained a call to DateTime.Now.

Basically, ActiveSharp's MethodInspector class gives you a lightweight API to examine the contents of method bodies, for whatever purpose you wish.

(*) Why not just use FxCop, aka Visual Studio Code Analysis? Well, if you are already using FxCop/Code Analysis on your project, and you are comfortable writing custom rules, then that's fine. However, if you're not already using FxCop, then you'll notice that it requires some setup: firstly to integrate it into your build process, since it doesn't run from inside your test suite, and secondly to turn off the built-in rules which you don't want it to enforce in your situation. Integrating ActiveSharp is really easy, since its just a managed DLL referenced by your unit test suit. Also, you may think twice, as I did, before writing FxCop custom rules - because the API for doing so is not documented and (apparently) is subject to change.

Last edited Dec 30, 2010 at 2:54 AM by johnrusk, version 11

Comments

No comments yet.