views:

117

answers:

1

UPDATE: I've changed the wording of the question. Previously it was a yes/no question about if a base class could be changed at runtime.

I may be working on mission impossible here, but I seem to be getting close. I want to extend a ASP.NET control, and I want my code to be unit testable. Also, I'd like to be able to fake behaviors of a real Label (namely things like ID generation, etc), which a real Label can't do in an nUnit host.

Here a working example that makes assertions on something that depends on a real base class and something that doesn't-- in a more realistic unit test, the test would depend on both --i.e. an ID existing and some custom behavior.

Anyhow the code says it better than I can:

public class LabelWrapper : Label //Runtime
//public class LabelWrapper : FakeLabel //Unit Test time
{
    private readonly LabelLogic logic= new LabelLogic();

    public override string Text
    {
        get
        {
            return logic.ProcessGetText(base.Text);
        }
        set
        {
            base.Text=logic.ProcessSetText(value);
        }
    }
}

//Ugh, now I have to test FakeLabelWrapper
public class FakeLabelWrapper : FakeLabel //Unit Test time
{
    private readonly LabelLogic logic= new LabelLogic();

    public override string Text
    {
        get
        {
            return logic.ProcessGetText(base.Text);
        }
        set
        {
            base.Text=logic.ProcessSetText(value);
        }
    }
}

[TestFixture]
public class UnitTest
{
    [Test]
    public void Test()
    {
        //Wish this was LabelWrapper label = new LabelWrapper(new FakeBase())
        LabelWrapper label = new LabelWrapper();
        //FakeLabelWrapper label = new FakeLabelWrapper();
        label.Text = "ToUpper";
        Assert.AreEqual("TOUPPER",label.Text);
        StringWriter stringWriter = new StringWriter();
        HtmlTextWriter writer = new HtmlTextWriter(stringWriter);
        label.RenderControl(writer);
        Assert.AreEqual(1,label.ID);
        Assert.AreEqual("<span>TOUPPER</span>", stringWriter.ToString());
    }
}

public class FakeLabel
{
    virtual public string Text { get; set; }
    public void RenderControl(TextWriter writer)
    {
        writer.Write("<span>" + Text + "</span>");
    }
}

//System Under Test
internal class LabelLogic
{
    internal string ProcessGetText(string value)
    {
        return value.ToUpper();
    }

    internal string ProcessSetText(string value)
    {
        return value.ToUpper();
    }
}
+3  A: 

This is simply not possible in .Net. You cannot alter compiled meta-data on the fly.

Think of all of the havoc this would cause. Pretend I had the following situation

class Example  {
  Label l = new LabelWrapper();
}

This code has executed and suddenly your code runs that switches the base type of LabelWrapper to be FakeLabel. This raises a number of very hairy problems, including but not limited to

  • What would happen to existing instances of Example?
  • What would happen to new instances of Example as this code is now completely invalid?
  • Number of security nightmares this introduces as I can now mutate my object on the fly in a way APIs can't account for.
JaredPar
I'll agree with you that there isn't a way to change the base class at run time. I'm still hoping to find the pattern to simulate it. But, I'm not sure (if it were possible) how this is any different than a factory that returns alternate implementations that have the same base class or support the same interface. The factory could choose a malicious implementation or a invalid implementation.
MatthewMartin