ShaderOp.com

ShaderOp.com

Hi there. I have moved to a new website at Mhmmd.org. I'm no longer updating this one, but I'm keeping it around so the Internet wouldn't break. See you there.

Using HtmlUnit to Test .NET Applications

I have been reading a lot about Behavior-Driven Development and test automation lately. Steven Sanderson’s blog proved to be a goldmine of practical information in that regard, specifically his post about BDD, SpecFlow, and ASP.NET MVC.

From my limited understanding of the subject, an ideal BDD test should verify a feature by running a series of tests against a real system using a real web browser. With the help of a suitable library, like WatiN for example, automating the web browser becomes a doable task. The problem is that as the number of tests increases, running them against a real browser becomes time consuming.

Mr. Sanderson lists another option in another blog post, and that is using a headless browser called HtmlUnit. It’s built with Java, but Mr. Sanderson has managed to get it running in .NET using IKVM, and I was able to replicate his experience successfully.

Close, But No Cigar

When working on a feature, I would like to be able to:

  • Use a real browser with BDD testing while developing the feature. This would allow me to actually see what’s going on and would make debugging easier.
  • Once I’m done working on it and the BDD tests are passing, I would like to switch to an headless browser for regression tests, since it would make the tests faster, and thus more likely to be maintained and ran with every build.
  • If a feature breaks during regression, I would like to switch back to using a real browser since, again, it would make debugging easier.

Using HtmlUnit and some other browser automation framework (e.g. WatiN) would mean that each test will have to be implemented twice, once for each framework, and that’s far from ideal.

Enter Selenium’s WebDriver

I have never used Selenium before, so my knowledge of their range of products is peripheral at best. But apparently they are implementing a new browser automation API called “WebDriver,” which will be part of the upcoming Selenium 2.0 release. This new API should allow programmatic automation of IE, Chrome, Firefox, and HtmlUnit using a uniform set of interfaces.

The latest public release is Alpha 5, which can be downloaded at this link. They have binaries for both Java and .NET, and the source code has libraries for Python and Ruby as well. I’ve only tried the .NET implementation, and found the Firefox driver to be quite solid, unlike the IE version, which was a bit too buggy for my taste. Hopefully it will all be sorted out by the final release.

Mr. Sanderson’s gives an example of how to use HtmlUnit in the blog post mentioned earlier. Here’s how the same example would be implemented in using WebDriver and Firefox:

[TestFixture]
public class GoogleSearchWithFirefox
{
    private IWebDriver _driver;

    [SetUp]
    public void SetUp()
    {
        _driver = new FirefoxDriver();
    }

    [TearDown]
    public void TearDown()
    {
        _driver.Close();
    }

    [Test]
    public void Can_Load_Google_Homepage()
    {
        _driver.Url = "http://www.google.com/";
        Assert.That("Google", Is.EqualTo(_driver.Title));
    }

    [Test]
    public void Google_Search_For_AspNetMvc_Yields_Link_To_AspDotNet()
    {
        _driver.Url = "http://www.google.com/";
        _driver.FindElement(By.Name("q")).SendKeys("asp.net mvc");
        _driver.FindElement(By.Name("btnG")).Click();

        // Should be on the results page now:
        var resultLinks =
            from element in _driver.FindElements(By.TagName("a"))
            let href = element.GetAttribute("href")
            where href.StartsWith("http://")
            let uri = new Uri(href)
            where uri.Host.ToLower().EndsWith("asp.net")
            select uri
            ;

        CollectionAssert.IsNotEmpty(resultLinks);
    }
}

I think the above code is self-explanatory and not too hard on the eyes. To run the same code against IE, it would be a simple matter of changing the following line:

_driver = new FirefoxDriver();

To:

_driver = new InternetExplorerDriver();

And everything should (at least once the final version of the IE driver is released) continue to work as before.

Now, as I have mentioned before, the WebDriver library does contain an implementation for an HtmlUnit-based driver, but unfortunately it’s only available in the Java version.

My Modest Contribution

So, I thought it would be a good idea to write a .NET implementation of the WebDriver API using HtmlUnit. I compiled the HtmlUnit JAR files to .NET assemblies, downloaded the WebDriver source code, and went to town. The WebDriver source code has about 340 unit tests. So far I’ve managed to get 303 of those to pass.

Using what I have so far, the above code could be rewritten as follows:

[TestFixture]
public class GoogleSearchWithHtmlUnit
{
    private IWebDriver _driver;

    [SetUp]
    public void SetUp()
    {
        _driver = new HtmlUnitDriver(true);
    }

    [TearDown]
    public void TearDown()
    {
        _driver.Close();
    }

    [Test]
    public void Can_Load_Google_Homepage()
    {
        _driver.Url = "http://www.google.com/";
        Assert.That("Google", Is.EqualTo(_driver.Title));
    }

    [Test]
    public void Google_Search_For_AspNetMvc_Yields_Link_To_AspDotNet()
    {
        _driver.Url = "http://www.google.com/";
        _driver.FindElement(By.Name("q")).SendKeys("asp.net mvc");
        _driver.FindElement(By.Name("btnG")).Click();

        // Should be on the results page now:
        var resultLinks =
            from element in _driver.FindElements(By.TagName("a"))
            let href = element.GetAttribute("href")
            where href.StartsWith("http://")
            let uri = new Uri(href)
            where uri.Host.ToLower().EndsWith("asp.net")
            select uri
            ;

        CollectionAssert.IsNotEmpty(resultLinks);
    }
}

As you can see, only the SetUp method changes. Everything else stays the same, the tests pass, and the they take a lot less time to run compared to the Firefox version.

I have the code—including the examples above—to BitBucket at http://bitbucket.org/shaderop/htmlunitdriver. I would appreciate any help that anyone reading this might offer, especially bug reports and patches. I know I’ll be using it myself at some point in the very near future, so I’ll try to work on it it as I go along.

Good luck.

2 Comments

# By Ahmed Ashour on August 12, 2010, 4:48 am

Nice work, what about contributing that to Selenium/WebDriver?

# By Mohammad on August 12, 2010, 2:53 pm

Thank you!

Actually I would love to sign the whole thing over to the Selenium guys, but I still can’t figure out their build system, so I’m unable to wrap my code in a nice little patch that they can just apply to their source tree.

Maybe at some point I’ll poke around their mailing lists and see if they would be interested.

Powered by: