Wednesday, September 9, 2009

Unit Testing SharePoint - Past, Present, and Sporm

As I described in SharePoint: The Wild West of Software Development there is a serious problem when you develop for SharePoint: ensuring quality through unit testing is really, really hard. And that's where a new open source tool just released today called sporm (SharePoint Object Relational Mapper) comes in. While sporm provides many benefits besides simplified unit testing I wanted to focus on this topic first, because sporm's approach, which models the entity framework in the way it supports POCO's, is a unique feature not available with other SharePoint tools like LINQ to SharePoint.

I'll start by describing the unit testing problem, then provide a conventional solution with mocking, then finish with the elegance of a sporm based solution.

Simple Unit Testing Turns Ugly

I would bet anyone that attempts to develop a tiered solution with SharePoint ends up with entities that look something like this:

public class Employee {
      public SPListItem ListItem { get; private set; }

      /// <summary>
      /// The constructor requires that you pass in a list item
      /// that is used by your strongly typed properties
      /// </summary>
      public Employee(SPListItem listItem) {
            ListItem = listItem;
      }

      /// <summary>
      /// This provides your entities strong typing to hide
      /// the native weak typing in SharePoint
      /// </summary>
      public virtual string LastName {
            get { return (string)ListItem["LastName"]; }
            set { ListItem["LastName"] = value; }
      }
}

And if you have some function that you'd like to unit test like this:

/// <summary>
///
Typically returns "[LastName], [FirstName]" but skips
/// the comma if either is missing
/// </summary>
public string GetNameFormatted() {
      bool noFirst = string.IsNullOrEmpty(FirstName);
      bool noLast = string.IsNullOrEmpty(LastName);
      if (noFirst && noLast) return "";
      if (noFirst) return LastName;
      if (noLast) return FirstName;
      return string.Format("{0}, {1}", LastName, FirstName);
}


Now you face a major problem: LastName is tightly coupled with SPListItem. And SPListItem only contains internal constructors making it unmockable. If you keep the tight coupling and pass an SPListItem into the constructor then you need a connection to SharePoint and an employee list (table) and you need to create an employee list item (record), then you test it, and finally you ought to clean up after yourself and delete the list item. Talk about a simple unit test turned fragile integration test. Yuck.

Mocking the Solution

So at this point you're undoubtedly thinking it's time to mock. To do this you'll have to make a public constructor that takes no arguments and mark your properties virtual. Then you can write a test like this:

[TestMethod]
public void TestGetNameFormatted_FirstAndLastExist_LastCommaFirst() {
      MockRepository mocks = new MockRepository();
      Employee mockEmployee = mocks.StrictMock<Employee>();
      Expect.Call(mockEmployee.FirstName).Return("Lee").Repeat.Any();
      Expect.Call(mockEmployee.LastName).Return("Richardson").Repeat.Any();
      mocks.ReplayAll();
      string actual = mockEmployee.GetNameFormatted();
      mocks.VerifyAll();
      Assert.AreEqual("Richardson, Lee", actual);
}

And then if you're like me you absolutely hate your unit tests. Because let alone that you had to modify your production code to accommodate unit testing, more importantly your unit tests are now unreadable. All of them. Even in simple methods without dependencies your unit tests contain almost as much plumbing code as actual test code. Enter sporm.

Unmocking with Sporm

Sporm is an open source tool released on codeplex that was developed by fellow Near Infinity employee Joe Ferner (who, incidentally, is one of the most brilliant developers I know). While this is a version 1.0 product Joe has been using it on his project and independently I've been using it on my project for several months now, so it is relatively mature.

So how it works is that at design time sporm will read from your SharePoint site and generate partial classes for each of your list items and each of your content types. The generated classes don't inherit from anything, giving you the ability architect your solution how you see fit. And the properties are auto-properties, making the classes just pure POCO. Here's an example if you're interested:

[SPOrmContentType(Name="Employee")]
public partial class Employee : ISPOrmContentType {
      public static class FieldTitles {
            public const string FirstName = "First Name";
      }

      public static class FieldStaticName {
            public const string FirstName = "FirstName";
      }

      [SPOrmIdField()]
      public virtual int Id { get; set; }

      [SPOrmField(Title = FieldTitles.FirstName, StaticName = FieldStaticName.FirstName)]
      public virtual string FirstName { get; set; }
}

Since generated entities look like that, your entities can now look like this:

public partial class Employee {
      public string GetNameFormatted() {
            bool noFirst = string.IsNullOrEmpty(FirstName);
            bool noLast = string.IsNullOrEmpty(LastName);
            ...
      }
}

Now if that isn't a picture of beauty to you then you haven't been working with SharePoint. But wait, I hear you asking, how does this have anything to do with SharePoint? How would an instance of an employee be able to retrieve the data in a SharePoint ListItem? The answer is in the DataContext. In your production code you write something like this:

public static Employee FindById(int id) {
      return MySPDataContext
            .GetCurrent()
            .GetList<TList>()
            .OfType<TContentType>()
            .FirstOrDefault(c => c.Id == id);
}

While it might not look so pretty at first you can hide these details in a base type and the nice thing is that it does handle SharePoint's ability to have multiple content types per list (which LINQ to SharePoint does not do, incidentally). But for now all you need to know is that when you return an Employee thorugh MySPDataContext it subtypes your Employee class and returns you a proxy at runtime. The proxy overrides each property to return you the appropriate value in the SPListItem. And if you instantiate an Employee directly (as you would in unit testing) then you can use it like a pure POCO. If you're familiar with the entity framework this should sound extremely similar. The result is that you can write your unit tests like this:

[TestMethod]
public void TestGetNameFormatted_FirstAndLastExist_CommaSeparate() {
      Employee employee = new Employee {
            FirstName = "Lee",
            LastName = "Richardson"
      };
      string actual = employee.GetNameFormatted();
      Assert.AreEqual("Richardson, Lee", actual);
}

No SharePoint dependencies. No mocking. Nothing but pure, unadulterated unit test. Now that a thing of beauty.

1 comment:

Anonymous said...

How is sporm on performance? Is there a lot of overhead? How does it compare to CAML?