White box unit testing with AspectDNG 13 Jan 2005



Last time, Tom was kidding me (softly) about the fact that we can do almost everything we want with AspectDNG. I begin to think he wasn't wrong...

Once again, here is a very, very complex system that should be hard tested:

// Main.cs

using System;

public class ComplexSystem {
    
    private int m_secret;
        
    public ComplexSystem() {
        m_secret = 0;
    }
    
    private void SecretOperation(int oper) {
        m_secret += oper;
    }
    
    // should always return a multiple of 2
    public int PublicOperation() {
        if ((m_secret % 2) != 0) {
            SecretOperation(1);
        }
        return m_secret;
    }
}

This can be safely compiled to a Main.dll.
Ok, this ComplexSystem is the central point of your next application, you NEED to test it. What can be better than NUnit ? But here is the problem, you would like to be sure that the secret operation have a correct behaviour, and that the m_secret field is well setted... Two way to do that, first you use boring Reflection, or you can even take a look behind your back, you ensure that you're alone, you change the visibility of all this, but you promise yourself that you'll change it as soon as tests passed. Also you may note that somewhere not to forgot, this is a SECRET operation.

What about introducing a new way of testing this ? Using AspectDNG ? Something I may call white box unit testing ? Here is one aspect to do that :

// Tests.cs

using System;
using NUnit.Framework;

// dummy complex system, may have been generated
public class ComplexSystem {
    public int m_secret;
    public void SecretOperation(int oper) {}
    public int PublicOperation() { return 0; }
}

[TestFixture]
public class TestComplexSystem {
    
    private ComplexSystem m_complexSystem;
        
    [SetUp]
    public void SetUp() {
        m_complexSystem = new ComplexSystem();
    }
    
    [Test]
    public void TestPublicOperation() {
        Assert.AreEqual(0, m_complexSystem.PublicOperation() % 2);
        m_complexSystem.SecretOperation(1);
        m_complexSystem.SecretOperation(1);
        m_complexSystem.SecretOperation(1);
        Assert.AreEqual(0, m_complexSystem.PublicOperation() % 2);
        Assert.AreEqual(4, m_complexSystem.PublicOperation());
    }
    
    [Test]
    public void TestFieldSecret() {
        Assert.AreEqual(0, m_complexSystem.m_secret);
        m_complexSystem.SecretOperation(1);
        Assert.AreEqual(1, m_complexSystem.m_secret);
    }
    
    [Test]
    public void TestSecretOperation() {
        int cursor = m_complexSystem.m_secret;
        m_complexSystem.SecretOperation(12);
        Assert.AreEqual(12, m_complexSystem.m_secret - cursor);
    }
}

What can we see there ? I use a dummy ComplexSystem, that looks like really close to the real one no ? It is just a trick, with that, I'll be able to compile this single file into a Tests.dll assembly. Once this is done, all I have to do it to inject the content of my TestComplexSystem into the real ComplexSystem. This can be done with the standard MetaAspect Insert from AspectDNG.
When weaving is done, after using a simple insert advice, here is the result :



"Et voila", the ComplexSystem is now a test fixture itself. Pretty simple no ?
And this sample works with Mono. Of course it is a very lightweight one, but hey, it looks good !

UPDATE: You can download the sources of the example here. Just compile as described in the post, and run AspectDNG -w on the AspectDNG.xml provided.

So, what do you think about this kind of unit testing ? I'm curious about you opinion !