Unit testing a web part

Before looking at how FakePoint can be used as a substitute for unit testing, we should first think about how we will go about building a unit test for a UI component such as a web part. Given the problems described earlier one might wonder if there was any hope at all in finding a way of unit testing a web part in an automated way. The strange thing here is that in the case of web part development the trivial example web part is in many cases not that far from the reality for a lot of production code. Let us suppose that we have a requirement for a web part that renders a SharePoint list as an HTML un-ordered list element (and for the sake of the example we will suppose that there is not an existing out-of-the-box SharePoint web part that can do this). Here is how we might implement that WebPart class:

using System.Web.UI.WebControls.WebParts;
using System.Web.UI;
using Microsoft.SharePoint;

namespace ClassLibrary1
{
  public class WebPart1 : WebPart
  {
    [WebBrowsable(), WebDescription("Name of list containing items")]
    public string ListName { get; set; }

    protected override void CreateChildControls()
    {
      SPList list = SPContext.Current.Web.Lists[ListName];
      if (list.ItemCount>0) Controls.Add(new LiteralControl("<ul>\r\n"));
      foreach (SPListItem item in list.Items)
        Controls.Add(new LiteralControl("  <li>" + item.Title + "</li>\r\n"));
      if (list.ItemCount > 0) Controls.Add(new LiteralControl("</ul>\r\n"));
    }

    protected override void RenderContents(HtmlTextWriter writer)
    {
      writer.WriteLine("<h2>Contents of List:</h2>");
      base.RenderContents(writer);
    }
  }
}

We have included output in both the RenderContents and the CreateChildControls methods for illustration – normally it would be one or the other. The question is; how to test the output? One approach that has been suggested is to bring the logic inside a method that does not depend on the user interface. For this to work the WebPart would have to be rewritten to be something like the following:

using System.Web.UI.WebControls.WebParts;
using System.Web.UI;
using Microsoft.SharePoint;

namespace ClassLibrary1
{
  public class WebPart1 : WebPart
  {
    [WebBrowsable(), WebDescription("Name of list containing items")]
    public string ListName { get; set; }

    public string CreateList()
    {
      System.Text.StringBuilder content = new System.Text.StringBuilder();
      SPList list = SPContext.Current.Web.Lists[ListName];
      if (list.ItemCount > 0) content.AppendLine("<ul>");
      foreach (SPListItem item in list.Items)
        content.AppendLine("  <li>" + item.Title + "</li>");
      if (list.ItemCount > 0) content.AppendLine("</ul>");
      return content.ToString();
    }

    protected override void RenderContents(HtmlTextWriter writer)
    {
      writer.WriteLine("<h2>Contents of List:</h2>");
      writer.Write(CreateList());
      base.RenderContents(writer);
    }
  }
}

Now that we have the important part of the web part in a public method we can build a test class that will call it. Assuming you are using a professional version of Visual Studio this is most easily achieved using the built-in MSTest unit testing framework. First you need to create a new test project in your solution. By default the WebPart will be added as a reference in the test project, but I prefer the approach of including the source file for the WebPart in the test project as a link for reasons that will become clear later. The example below shows the resulting simplified tests class:

using ClassLibrary1;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestProject
{
  [TestClass]
  public class WebPart1Test
  {    
    [TestMethod]
    public void CreatesUnorderedList()
    {
      WebPart1 webpart = new WebPart1();
      webpart.ListName = "Tasks";
      string result = webpart.CreateList();
      string expected = @"<ul>
  <li>Build a set of test cases</li>
  <li>Implement code to satisfy test cases</li>
</ul>
";
      Assert.AreEqual(result, expected);
    }
  }
}

The same results can of course be achieved using other testing frameworks. For example, NUnit can be used by including the NUnit framework in the project references, changing the TestClass and TestMethod attributes to TestFixture and Test respectively, and modifying the Assert statement.

The problem with this approach is that it requires us to design the WebPart for the purpose of the test class. Apologists for TDD often assert that whenever code has to be written in a particular way in order to make it testable, that this is somehow the “right” way to code, and to do it any other way would be bad programming practice. It can be quite upsetting to find that the way you have done something for the last 30 years is now dismissed by relative newcomers as an “anti pattern” or, the ultimate insult; a “code smell”. Unfortunately the way a WebPart is constructed is pretty much fixed by the ASP.net framework and it would be nice if we could make automated unit testing work without having to re-invent it. What we would really like to do is call the RenderContents method of the WebPart directly and examine the returned mark-up.

Herein lies the first problem. We cannot simply create an HTML writer object and pass it to the RenderContents method:

HtmlTextWriter writer = new HtmlTextWriter();
Webpart.RenderContents(writer);

because RenderContents is a protected member of the WebPart class. One solution to this is to use the built-in ability of Visual Studio to generate an accessor class to the WebPart. This happens automatically if you right-click on the WebPart class itself in visual studio and select the option to generate a test. This does solve the problem but it is relies on taking the output of the WebPart project as a dependency in the test project. This prevents us from later building the WebPart with different options or against different versions of dependencies. It can also give problems later when we start refactoring code as the generated accessor classes can get “out of sync”.

Because the method in question is protected, rather than private, another option is available to us. This is to derive a new class from the WebPart class and add accessor methods that are public, thus avoiding the need to pollute the WebPart class with test code. However, an even easier way is to simply derive the test class itself from the WebPart. I think this is quite an elegant solution and means that a test class can be created which gives full test coverage of the WebPart without needing any changes to the WebPart itself. The resulting test class is as follows (the WebPart under test can be either of the versions above):

using ClassLibrary1;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Web.UI;
using System.IO;

namespace TestProject2
{
  [TestClass]
  public class WebPart1Test : WebPart1
  {    
    [TestMethod]
    public void RenderContents_Tasks()
    {
      ListName = "Tasks";
      StringWriter sw = new StringWriter();
      HtmlTextWriter writer = new HtmlTextWriter(sw);
      EnsureChildControls();
      RenderContents(writer);
      string expected = @"<h2>List Contents:</h2>
<ul>
  <li>Build a set of test cases</li>
  <li>Implement code to satisfy test cases</li>
</ul>
";
      Assert.AreEqual(sw.ToString(), expected);
    }
  }
}

The call to EnsureChildControls is necessary to make the base class calls the CreateChildControls method, in case there is an override in the derived class. This test works regardless of whether the web part content is rendered by overriding the RenderContents method or the CreateChildControls method. It is also possible to use ASP.NET web controls in building the HTML content. The important thing here is that it has not been necessary to change the code of the web part itself in order to test it.

A number of advocates of TDD have proposed that a component such as a web part that includes UI should be built following the MVP (model view presenter) pattern or some variation thereof. This is often simultaneously advocated as a best practice in its own right, although one suspects that the only real motivation is to facilitate unit testing. The resulting code involves interfaces for view and presenter and separate classes for two or three components. In some cases this may be appropriate where there is substantial logic in these three areas and there is a genuine need to separate these “concerns”. But I am of the view that the architecture should not be dictated by the testing methodology. It may well be that in some cases the MVP or MVVP or MVC architecture is justified and in this case these elements can be unit tested independently of one another. However in the present case (which is not untypical in complexity for a well thought out WebPart) I think that an MVP pattern involving separate classes would be overkill, and would generate a lot of code that did nothing, and tests that tested nothing.

Last edited Jan 11, 2010 at 5:04 PM by flosim, version 3

Comments

No comments yet.