man thinking

Unit Testing - Supplying the SPContext

In an earlier article I looked at a way of creating a suite of unit tests for a web part without changing the design or architecture of the web part itself. My view is that the structure of the web part code should not need to be subserviant to the requirements of unit testing.

One of the problems in designing the unit tests is that you often find your web part makes use of the SPContext of the current request. This creates a problem because when the unit test runs the web part methods are getting called outside the context of a web page. In order to get this to work we are going to have to either create a fake SPContext object, or find a way of setting up a context object so that everything works as if the web part was being rendered in a page as normal.

Fortunately, there is a way of doing this without needing to resort to the creation of fake objects. In order to achieve this we need to create the underlying HttpContext object, because this is what the SPContext wraps. We can then use this to create the SPContext. We'll assume that you already have a site collection created on your local machine. If necessary you could set this up programmatically, along with any required lists, in the ClassInitialize method of your test class. Setting up the SPContext can be done in each test method, but an easier way is to create a TestInitialize method. This will be called automatically before each test is run, so it is a good place to set up any context required for each test.

The following code is required to set up the context:

site = new SPSite("http://localhost/");
web = site.RootWeb;
HttpRequest request = new HttpRequest("", web.Url, "");
request.Browser = new HttpBrowserCapabilities();
HttpContext.Current = new HttpContext(request, 
                      new HttpResponse(new StringWriter()));
HttpContext.Current.Items["HttpHandlerSPWeb"] = web;

The key step here is to set the HttpContext property, which means that you can use SPContext.Current.Web in the web part to get the current SPWeb object (i.e. the current site).

Once you have set this all up, your test class will look something like the following:

namespace TestProject
{
  [TestClass]
  public class MyWebPart_Test : MyWebPart
  {
    public static SPSite site;
    public static SPWeb web;

    [ClassInitialize]
    public static void ClassInitialize(TestContext tc)
    {
      // Do any site or list creation for all tests in the test class
    }

    [TestInitialize]
    public void TestInitialize()
    {
      site = new SPSite("http://localhost/");
      web = site.RootWeb;
      HttpRequest request = new HttpRequest("", web.Url, "");
      request.Browser = new HttpBrowserCapabilities();
      HttpContext.Current = new HttpContext(request, 
                            new HttpResponse(new StringWriter()));
      HttpContext.Current.Items["HttpHandlerSPWeb"] = web;
    }

    [TestCleanup]
    public void TestCleanup()
    {
      site.Dispose();
      site = null;
    }

    [TestMethod]
    public void MyWebPart_Generates_Correct_Content()
    {
      StringWriter sw = new StringWriter();
      HtmlTextWriter writer = new HtmlTextWriter(sw);
      EnsureChildControls();
      RenderContents(writer);
      Assert.IsTrue(sw.ToString().Contains("Hello World!"));
    }
  }
}