Sunday, 22 April 2012

SharePoint 2010 and unit tests (from Visual Studio 2010/MSTest.exe)

It’s been possible to run unit tests/integration tests for SharePoint 2010 within Visual Studio 2010 since VS2010 SP1, but I’m starting to wonder if the steps to enable this are widely known. Whilst writing another article, I did an internet search and didn’t find any useful guides on page 1 of my results. Indeed, many of the top search results contain information which is now out-of-date, such as the “Unit testing with SharePoint 2010 development” article on the usually good www.nothingbutsharepoint.com. However, I won’t be the first to write about this and I suspect the info is out there if you really go looking – but I’m going to do my bit and write the steps down here too.

The key issue here is being able to call into the SharePoint API from a unit test (usually referred to as an integration test). ‘Pure’ unit tests which do not call SharePoint objects have always been fine. The prerequisite for being able to run SharePoint integration tests are:

From there, just follow the process below.

The process - running SP2010 unit/integration tests from Visual Studio 2010

  1. Configure Visual Studio 2010  to allow Test projects (i.e. VS projects which contain tests) to be targeted for .NET 3.5 (the framework version SharePoint 2010 uses):

    The framework version for a Visual Studio project can be configured in the project properties, but I’ve always needed to perform a one-off task (per dev VM) – edit the devenv.exe.config file to allow VS to retarget test projects. These steps are listed on MSDN at Possible Additional Steps to Enable Re-targeting of Test Projects to .NET Framework 3.5, but for completeness I’ll summarise them here:

    - Close Visual Studio if open
    - Open Windows Explorer to C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\
    - Take a backup copy of devenv.exe.config
    - Open the original devenv.exe.config file in Notepad
    - Paste in the entries listed in the ‘Possible Additional Steps..’ link above. Take note that there is an appSettings key as well as the main set of bindingRedirect entries
    - Save the config file
    - Reopen Visual Studio

  2.  Create a Test project and ensure it gets targeted to .NET 3.5:

    There are a couple of routes to doing this, made slightly confusing that the ‘Test Project’ template which targets 3.5 is listed underneath a confusingly-named ‘Test Documents’ heading in the dialog. Either use that one (and check it does target 3.5), or use the main ‘Test Project’ template and go on to retarget it from .NET 4 to .NET 3.5. I’ll show that process here:

    CreateTestProject

    Once the project has been created, go into the project properties and on the Application tab, ensure the Target Framework setting is ‘.NET Framework 3.5’:

    RetargetTestProject

    You’ll see a dialog warning that the project must be closed and reopened – click ‘Yes’ to do this.

  3. Ensure the ‘Platform target’ of the project is set to ‘Any CPU’:

    TestProject_AnyCPU
  4. Ensure the .testsettings file specifies tests should run in a 64-bit process:

    You should now have a couple of .testsettings files in your Visual Studio solution. The deafult one is called Local.testsettings, and we need to edit this one – but note if you use any other file, you’ll need to edit that too. On the Hosts tab, ensure the ‘Run tests in 32 bit of 64 bit process’ option is set to 64-bit:

    TestSettings_64bit

The result

You should now be able to write tests which call into the SharePoint API. For example, here’s a slightly theoretical test showing I can call a utility class which uses SPSite/SPWeb objects:

   1: [TestMethod()]
   2: public void GetRootTeamSiteTitleTest()
   3: {
   4:     string expected = "BVT test"; 
   5:     string actual;
   6:     actual = CobCiHelper.GetRootTeamSiteTitle();
   7:     Assert.AreEqual(expected, actual);
   8: }

..where the code in the CobCiHelper class being called looks like this:

   1: public static string GetRootTeamSiteTitle()
   2: {
   3:     string title = null;
   4:     using (SPSite site = new SPSite(Urls.Homepage))
   5:     {
   6:         using (SPWeb web = site.RootWeb)
   7:         {
   8:             title = web.Title;
   9:         }
  10:     }
  11:     return title;
  12: }

If your test should pass, you’ll see something like this (but either way your text should execute successfully):

TestPassed

Running SharePoint integration tests within TFS automated builds

The process above helps you run tests manually from within Visual Studio, but note that it does not help if you want to run the same tests from within an automated build. Unfortunately, integration tests which call the SharePoint API are not currently supported in Team Foundation Server 2010/Visual Studio 2010. ‘Pure’ unit tests, or those which use a mocking framework such as Typemock can be used though. Let’s hope this challenge goes away in the next wave of technologies.

6 comments:

Half said...

Thanks for the detailed information. I was going mad, because applying SP1 did not make any difference. I would say almost all developer will have to manually edit the desenv.exe.config, who doesn't have at least one pluggin or addon?

One detail: the link you provided (Additional steps...) points to the VS 2012 article, where that section does not exist. The correct link is thus http://msdn.microsoft.com/en-us/library/gg601487(v=vs.100).aspx

westerdaled said...

Chris

Good post.. I am having getting more rigorous with unit testing as my Clients are increasingly asking me to code around many sp2010 short comings ;-)/. Do you personally (type) mock stuff before going down the route you describe in the post?
BTW see you on the 18th..

Venkatesh Kumar said...

Thanks for the informative post. i have two "devenv.exe.config" files , one with the "devenv.exe.config with GUID". i done the "Possible additional steps" in the second one and its working.

Thanks a lot.

Maarten said...

Hi Chris,

Thanks for this article. I'm trying to implement unit tests in a VS2012 environment. It seems that targeting the correct framework isn't an issue in this version of VS.

Creating a unit test which gets a reference to an SPSite works (I'm using a 'fake' SPContext and use the new SPSite(url, site.UserToken) constructor). However, when I try to update a SPListItem in the unit test, I receive the well known 'Updates are currently disallowed on GET requests.' error.

A solution would be to modify the original code by adding the 'web.allowunsafeupdates = true' line, however, it doesn't feel right to change the original code, for unit tests.

Do you have an idea how to solve this problem?

Thanks!

Chris O'Brien said...

@Maarten,

I agree, amending the code being tested isn't ideal. Actually I'm surprised you get this error in a unit test - is HttpContext set to null? If not, try doing that and seeing if you still get the error.

If I remember rightly, the security check does look for something in HttpContext.

Cheers,

Chris.

Maarten said...

Hi Chris,

Thanks for your reply!

In my current solution, setting the HTTPContext to null isn't quite an option. I intentionally create a 'fake' HTTPContext in my test so that i can use it's inner properties to store a web object and use that to retrieve a 'fake' SPContext in my application. I create the fake context like this:

========

HttpRequest request = new HttpRequest("", web.Url, "");
request.Browser = new HttpBrowserCapabilities();
HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter()));

// SPContext is based on SPControl.GetContextWeb(), which looks here
if (HttpContext.Current.Items["HttpHandlerSPWeb"] == null)
HttpContext.Current.Items["HttpHandlerSPWeb"] = web;

return SPContext.Current;

========

Setting the HTTPContext to NULL results in a nullreference in my application, which gives me a big red dot in my test explorer :-)

Do you have another suggestion to get around the 'Updates are currently disallowed on GET requests.' error, or how to create a 'fake' spcontext without the need for an HTTPContext?

Thanks!