In some projects lately I've been faced with the question about how to unit-test SharePoint code.
I've been experimenting with unit testing in Visual Studio 2008 and particularly testing SharePoint code.
What do I mean by "SharePoint code" ? I mean server-side .NET code written against the SharePoint API (WSS 3.0 or MOSS assemblies, most notable the Microsoft.Sharepoint.dll).
There are of course several ways to code against SharePoint. You can establish the SharePoint context (which site, page, user etc. in SharePoint) completely from within your code as this example shows:
public static string GetSiteTitle(string url)
SPSite site = new SPSite(url);
string title = site.RootWeb.Title;
This code does not require a SharePoint context to run. That means that you don't need to run this code trough a SharePoint site/page, webpart or service. It can easily be invoked from a windows forms or console app. This kind of SharePoint code can easily be unit tested just like any other code.
Another way to code against SharePoint is to use the SharePoint context object. Whenever code is invoked on a page, control or webpart within a SharePoint site (using ASP.NET/IIS), the SharePoint context is available - like the HTTP Context. The SharePoint context contains information about the current site, page, user, etc. To get the title of the current SharePoint site using the SharePoint context, you can use the following code:
public static string GetSiteTitle()
SPSite site = SPContext.Current.Site;
string title = site.RootWeb.Title;
This method must be invoked through a SharePoint site because it uses the SPContext object. If you try to invoke the code from a console or windows forms application, you will get an exception because SPContext is null.
So, how can the code be unit tested in Visual Studio when you're not able to invoke the method without the SharePoint context? The answer is to bring the context to Visual Studio!
The usual way to implement a unit test would be something like this:
public void SayHelloTest()
string url = http://localhost/site;
string expected = "Value";
actual = MyClass.GetSiteTitle(url);
If you create a test that calls a method which is using SPContext, you will get an exception in the test result. This is an example of two tests calling methods which is not using the SharePoint context, and one that is:
To be able to unit test code using the SharePoint context, you can hook up the host adapter for ASP.NET in the unit test. This is an example:
public void SayHelloMossTest()
string input = "MOSS";
string expected = "Hello MOSS from site Home";
actual = SayMOSS.SayHelloMoss(input);
After adding HostType and UrlToTest, the test is invoked using the host adapter for ASP.NET. Running the tests then would be able to host the SharePoint context as well as the ASP.NET HTTP Context:
Another way to use the ASP.NET host adapter for ALL tests is to specify it in the test configuration:
Go to Test -> Edit Test Run Configuration and select a configuration.
Now, when running tests, all tests will be executed with the ASP.NET host adapter.
You can also use the SharePoint context directly in the test like this example shows:
public void GetContextInfo()
string expected = "domain\\user";
string name = Microsoft.SharePoint.SPContext.Current.Web.CurrentUser.LoginName; // get the login name of the current user
So, this definitely shows that it is possible to unit test SharePoint code, however there will be some limitations:
- You cannot execute tests against remote servers. This means that SharePoint must be installed on the machine running the tests. This might be the case for developer workstations, but might not be the case for build server(s).
- Code needs to run with full trust. You need to enable full trust in web.config for the SharePoint web application
- Debugging not as easy when using host adapter. See more below.
- Tests are invoked with the current logged on user in Visual Studio or the service account for msbuild. I haven't found a way to execute tests against SharePoint with a particular users credentials.
What about debugging?
Since the test code invoked using the ASP.NET host adapter runs in the host adapter, you cannot simply add a breakpoint in the test code and expect it to break when running the test. To be able to debug hosted tests use the following procedure:
1. Add a breakpoint in the code
2. Select Debug -> Attach to process
3. Attach to the w3wp.exe process
4. Instead of hitting F5 or clicking the "Run" button (the green arrow icon) to run the test, select "Test" -> "Run" in the main menu. This will run the tests and the debugger will be invoked on a breakpoint using the host adapter.