By Chris R. Chapman at February 15, 2006 04:48
Filed Under: better practices, sharepoint, tdd, unit testing

One of the primary demotivators that I've been encountering among SharePoint developers when it comes to unit testing their code is how to do it easily.  TDD is a conceptual stretch for most folks to begin with, never mind the complexities that can arise when testing code that has to run under a secure web app framework like SharePoint.

So, when I was tapped last fall to create an application to migrate documents into a SharePoint 2003 server taxonomy, I took the opportunity to stop the kvetching, get down to brass tacks and write some tested code!  In this post, I'll explain how I used the NUnit test suite to build a sophisticated SharePoint test harness.

To keep things brief, I'll be assuming that the reader is familiar with SharePoint development and NUnit test authoring.

Getting Started:  The Development Environment

Here at the office we use Microsoft Virtual Server images to host a development instance of Windows 2003 with WSS and SPS installed.  Generally speaking, we have a solution set up in VSS with post-build event scripts configured for each project that copies the compiled binaries to the server image via a mapped network drive -- we use these after doing the initial deployment of web part packages with the attendant stsadm -o addwppack rigamarole.

As part of a test-ready development server image, I also install NUnit to the server since the test assemblies need to be run under the SharePoint app domain.  Now, I've noticed that there are remoting methods built-in to NUnit's core framework, and someday I hope to migrate to a model where I can run the test gui locally -- for now, I'm more interested in writing tests.

The Evolution of the BaseSharePointUnitTestFixture

At first, my NUnit test classes weren't very elegant -- they were designed to test the functionality of a class under development.  As such, there was a lot of common functionality that I began to notice as the test classes evolved.  For example, I found that I was frequently using SPSite, SPWeb, PortalSite, PortalContext and TopologyManager objects to test my assemblies, as well as attendant methods for setting them up.  These were moved into the base class:

    1 /// 
    2 /// This class defines an NUnit compatible test fixture assembly for
    3 ///
running unit test methods.
    4 ///

    5 [TestFixture]
    6 public class
BaseSharePointUnitTestFixture
    7 
{
    8    protected string _baseSiteUrl = ""
;
    9    protected string _webUrl = ""
;
   10    protected SPSite _spSite = null
;
   11    protected SPWeb _spWeb = null
;
   12 
   13    protected string _portalUrl = ""
;
   14    protected SPSite _spPortalAreaSite = null
;
   15    protected SPWeb _spPortalAreaWeb = null
;
   16    protected PortalSite _spPortalSite = null
;
   17    protected PortalContext _spPortalContext = null
;
   18    protected TopologyManager _spTopologyMgr = null
;
   19    protected Area _spTargetArea = null
;
   20    protected Guid _spTargetAreaGuid;

Notice that I scoped the members as protected so as to allow descendant classes easy access to them. I then added methods such as InitializeSPObjects for simple initialization:

   21 /// 
   22 /// Initializes the test fixture's SPSite and SPWeb objects using the URLs specified
   23 ///
by the _baseSiteUrl and _webUrl properties.
   24 ///

   25 protected void InitializeSPObjects()
   26 
{
   27 
   _spSite = GetSPSiteAreaObject(_baseSiteUrl);
   28 
   29    if(_webUrl != null
&& _webUrl.Trim().Length > 0)
   30 
      spWeb = _spSite.OpenWeb(_webUrl);
   31    
else
   32 
      _spWeb = _spSite.OpenWeb();
   33 }

Impersonation Support

Often, when running test code you may find yourself unable to perform certain tasks against the SharePoint object model because you do not possess sufficient rights.  Sometimes, the assemblies under test require some level of authentication agnosticism.  Whatever the motivation, you're going to need to run code under the credentials of the service account -- I added this functionality to my base class by referencing the System.Runtime.InteropServices, System.Security.Principal and System.Security namespaces and adding some methods and Win32 kernel references:

   34 protected static WindowsIdentity _connectedUser = null;
   35 protected static WindowsImpersonationContext _impersonatedServiceAccount = null
;
   36 [DllImport("advapi32.dll"
)]
   37 protected static extern int
RevertToSelf();
...
   38 /// 
   39 /// Starts running current thread under the service account identity.
   40 /// 
   41 protected static void StartRunAsServiceAccount()
   42 {
   43    _connectedUser = WindowsIdentity.GetCurrent();
   44    RevertToSelf();
   45    _impersonatedServiceAccount = WindowsIdentity.GetCurrent().Impersonate();
   46 }
...
   47 /// 
   48 /// Stops running the current thread under the service account identity.
   49 /// 
   50 protected static void StopRunAsServiceAccount()
   51 {
   52    if(_impersonatedServiceAccount != null)
   53       _impersonatedServiceAccount.Undo();
   54 
   55    if(_connectedUser != null);
   56       _connectedUser.Impersonate();
   57 }
 

Using these methods, I can easily bracket some secure method calls with StartRunAsServiceAccount and StopRunAsServiceAccount where needed. This is really useful when stashing things into a document library or setting up tests using the DirectoryServices namespace for interacting with ActiveDirectory.

Now for something controversial...

A significant cause of debate in the TDD world is whether test assemblies should be allowed to violate the principle of encapsulation for assemblies under test.  In other words, should we be able to access private properties and methods.

Look:  I won't get pious on you.  I believe in good OO design as much as the next guy, but sometimes being able to check the state of a private member or assert against a private method serves a purpose in setting up a test case (or tearing one down).  To that end, I added two methods that I modded from a blog entry by Arne Vandamme (see my 12/07/05 blog entry) that allows for accessing private methods and properties:

   58 /// 
   59 /// Returns the value of an object's private member field.
   60 ///

   61 ///
   62 ///
   63 ///
   64 protected object GetPrivateField(object instance, string fieldName)
   65 
{
   66 
Type t = instance.GetType();
   67 
   FieldInfo f = t.GetField(fieldName,
   68 
   BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
   69 
   70    return
f.GetValue(instance);
   71 
}
...
   72 /// 
   73 /// Invokes a private method for an object, returning any values or objects.
   74 /// 
   75 /// 
   76 /// 
   77 /// 
   78 /// 
   79 protected object ExecutePrivateMethod(object instance, string methodName, params object[] paramList)
   80 {
   81    Type t = instance.GetType();
   82 
   83    if(paramList != null)
   84    {
   85       Type[] paramTypes = new Type[paramList.Length];
   86       for(int i=0; i < paramList.Length; i++)
   87       paramTypes[i] = paramList[i].GetType();
   88 
   89       MethodInfo m = t.GetMethod(methodName,
   90          BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
   91          null,
   92          paramTypes,
   93          null);
   94 
   95          return m.Invoke(instance, paramList);
   96    }
   97    else
   98    {
   99        MethodInfo m = t.GetMethod(methodName,
  100           BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
  101 
  102        return m.Invoke(instance, null);
  103    }
  104 }
 
These are really easy to use: Just pass in an instance of the test object, the method name you want to invoke and parameters and you're off to the races.  As I mention, this is really useful for reaching in and checking the state of your objects when no other means is available.  Yes, it's bad, but not as bad as the kvetching about it and not doing any TDD because it's holding up your state of mind.

Cleaning things up

As good developers, we're always wanting to make sure that we clean up after ourselves, and this base class is no exception.  It's always good form to include some methods for closing off our SharePoint objects so as to not tie them up unnecessarily:

  105 /// 
  106 /// Releases the objects used for testing the portal.
  107 ///

  108 protected void ClosePortalObjects()
  109 
{
  110    if(_spPortalAreaWeb != null
)
  111 
       _spPortalAreaWeb.Close();
  112 
  113    if(_spPortalAreaSite != null
)
  114 
       _spPortalAreaSite.Close();
  115 
  116    _spPortalAreaWeb = null
;
  117    _spPortalAreaSite = null
;
  118    _spPortalSite = null
;
  119    _spPortalContext = null
;
  120    _spTargetArea = null
;
  121    _spTopologyMgr = null
;
  122 
}
...
  123 /// 
  124 /// Closes the SPSite and SPWeb member objects.
  125 /// 
  126 protected void CloseSPObjects()
  127 {
  128    if(_spWeb != null)
  129        _spWeb.Close();
  130    if(_spSite != null)
  131        _spSite.Close();
  132 }
 

Summary

In today's post I've provided a brief overview of how I approach unit testing SharePoint applications via the construction of an NUnit test fixture base class that serves as the foundation for descendant test classes.  This base class aggregates common methods and properties that make writing test cases a lot easier for developers, removing one of the chief mental blocks when it comes to unit testing SharePoint code.

In an upcoming post, I'll show how to use this class to construct some basic unit tests for interacting with the SharePoint object model.  For now, I welcome any and all feedback.  How are you handling TDD with SharePoint or ASP.NET?  Let's share the knowledge!

Download sample code for this article.

Comments are closed

About Me

I am a Toronto-based software consultant specializing in SharePoint, .NET technologies and agile/iterative/lean software project management practices.

I am also a former Microsoft Consulting Services (MCS) Consultant with experience providing enterprise customers with subject matter expertise for planning and deploying SharePoint as well as .NET application development best practices.  I am MCAD certified (2006) and earned my Professional Scrum Master I certification in late September 2010, having previously earned my Certified Scrum Master certification in 2006. (What's the difference?)