Introducing Html Utilities for Silverlight

Shout it kick it on DotNetKicks.com

I have just released an early version of Html Utilties for Silverlight on CodePlex.  This article will explain the whats and hows of this library.

Who is this library for?

The primary target for this library is someone who:

  1. Develops software in Silverlight
  2. Uses the Html DOM libraries in Silverlight

What problem does this library solve?

In the first version of this library, it is all about testability.  Lets say I have a (very simple) method I want to test.  The code looks like this:

public class HtmlGenerator
{
    public static HtmlElement CreateImage(string url)
    {
        var img = HtmlPage.Document.CreateElement("img");
        img.SetAttribute("src", url);
        return img;
    }
}

Trying to write a test for this becomes very difficult.  There are two problems here:

  1. HtmlPage is static, and testing CreateImage with this static is not a unit test.  It relies on the browser.
  2. Unless your tests are executing in the browser, there is no browser to fall back on.  HtmlPage.Document fails with an exception that the HtmlPage is not enabled.

The test I WANT to write includes the ability to stub out the call to CreateElement and verify that the source was set.  Using NUnit and Moq, it would look something like this:

// JUST AN EXAMPLE OF WHAT I WANT TO DO.  SEE ACTUAL TEST FURTHER DOWN
[Test]
public void Test_Create_Image_Creates_Element_And_Sets_Src()
{
    var mockImage = new Mock<HtmlElement>();
    HtmlPage_Document.Setup(doc => doc.CreateElement("img"))
        .Returns(mockImage.Object);

    var newImage = TableGenerator.CreateImage("AbcDef.png");

    Assert.That(newImage, Is.EqualTo(mockImage.Object));
    mockImage.Verify(img => img.SetAttribute("src", "AbcDef.png"));
}

I can’t do anything like this because the HtmlPage is static and there are no interfaces on any of the classes in the DOM.

This is a very contrived example for the purpose of illustration, but I have had to use this approach in many cases from loading new windows, to inserting data into the DOM.

What does this library do?

This library wraps all of the HtmlPage classes with wrappers that implement interfaces.  For instance, HtmlPage is wrapped around a proxy that implements IHtmlPage.  HtmlElement has a proxy wrapper that implements IHtmlElement.  By using this approach, you can substitute anything you want in the Html DOM when you test.  This may be any test object that implements the interfaces.  I prefer to use Moq, but there are several other approaches to testing now that the system has interfaces to replace.

How do I use the library?

In an attempt to make the migration as simple as possible, any code that uses HtmlPage.* is just replaced with Html.Page.* (notice the dot after Html). 

So, in the example above, the code would look like this now:

public class HtmlGenerator
{
    public static IHtmlElement CreateImage(string url)
    {
        var img = Html.Page.Document.CreateElement("img");
        img.SetAttribute("src", url);
        return img;
    }
}

Notice that the only differences between these two methods are:

  1. The return type is IHtmlElement instead of HtmlElement
  2. HtmlPage.Document is replaced with Html.Page.Document

Next, my entire test fixture looks like this:

public class HtmlGeneratorTests
{
    private MockHtml _mockHtml;

    [SetUp]
    public virtual void SetUp()
    {
        _mockHtml = new MockHtml();
    }

    [TearDown]
    public virtual void TearDown()
    {
        _mockHtml.TearDown();
    }

    [Test]
    public void Test_Create_Image_Creates_Element_And_Sets_Src()
    {
        var mockImage = new Mock<IHtmlElement>();
        _mockHtml.Document.Setup(doc => doc.CreateElement("img"))
            .Returns(mockImage.Object);

        var newImage = HtmlGenerator.CreateImage("AbcDef.png");

        Assert.That(newImage, Is.EqualTo(mockImage.Object));
        mockImage.Verify(img => img.SetAttribute("src", "AbcDef.png"));
    }
}

You may be noticing that this test references a class named MockHtml.  This is a class that implements the HtmlPage system using Moq, but you can do this in any way. 

The most important thing to notice in this class is that we inject the IHtmlPage into the system using Html.SetHtmlPageForTesting() when we construct the object, and we set it back to null when we are complete.  This is required so that when Html.Page is called, it calls your test implementation of IHtmlPage instead of the wrapper around the actual browser.

public class MockHtml
{
    public Mock<IHtmlPage> Page { get; private set; }
    public Mock<IBrowserInformation> BrowserInformation { get; private set; }
    public Mock<IHtmlDocment> Document { get; private set; }
    public Mock<IHtmlWindow> Window { get; private set; }

    public MockHtml()
    {
        Page = new Mock<IHtmlPage>();
        BrowserInformation = new Mock<IBrowserInformation>();
        Document = new Mock<IHtmlDocment>();
        Window = new Mock<IHtmlWindow>();

        Page.SetupGet(page => page.BrowserInformation).Returns(BrowserInformation.Object);
        Page.SetupGet(page => page.Document).Returns(Document.Object);
        Page.SetupGet(page => page.Window).Returns(Window.Object);

        Html.SetHtmlPageForTesting(Page.Object);
    }

    public void TearDown()
    {
        Html.SetHtmlPageForTesting(null);
    }
}

How do I get it?

Version 0.1 has been released on CodePlex.  The source code is available on this site, along with the release DLL.  The Source project is broken into 4 projects: The HtmlUtilities project, a Silverlight test, a Web Site to host the Silverlight test, and a set of unit tests, showing how to mock out everything you would want to do with the library.

Future plans

I have some thoughts for where this library should go.  I want to aim towards a very easy to use API for manipulating the DOM through Silverlight.  My original thought was that the roadmap might look something like:

0.1 — Wrappers around existing objects

0.2 — Extensions to existing objects (HtmlLink, HtmlImage, HtmlDiv, etc)

0.3 — Helper utilities for doing more complex actions on the HTML

0.4 — Cross-browser issues abstracted away?

0.5 — Anything else??

1.0 — World domination???

At least, in the next few versions, it would be nice to be able to do something like create a table using code that is much more clear than what it takes now.  Possibly something that looks more like:

Html.New<Table>("Border=1",
    Html.New<THead>(
        Html.New<TR>(Html.New<TH>("Header 1"), Html.New<TH>("Header 2")),
    Html.New<TBody>(
        Html.New<TR>(Html.New<TD>("Data 1.1"), Html.New<TD>("Data 1.2")),
        Html.New<TR>(Html.New<TD>("Data 2.1"), Html.New<TD>("Data 2.2")))));

The Table type would have static properties on it instead of the more awkward way of doing it now. I dunno… I’m just playing at this poing. Would that be useful?

Leave a Reply