SharePoint: Isolating Test Code

The Visual Studio templates for SharePoint projects default to GAC deployment. This means that all the code you write is going to be installed to the Global Assembly Cache (GAC). At first sight this seems pretty useful. No matter from which application you want to access your assemblies, they will be found automatically. SharePoint runs your code in different contexts, from IIS web applications to timer jobs, so this has actual value. But the GAC is no silver bullet, and you will notice that when you start writing tests. .NET prefers to load assemblies from the GAC, no matter what. If you deploy your class library to the GAC in your WSP, then change the source code and run the tests, .NET will load the previous version from the GAC, even if you updated the assembly your test is linked to. This can happen in more or less obvious ways. If you add a new class method, you will get a runtime linker error. It might not be clear that the outdated version in the GAC is the cause, but at least it is out of question that something went wrong. If you only changed the implementation, this can lead to headaches. You can step through your code in the debugger, but it behaves not as stated there. You recompile, and on the next run it still does things you thought you fixed a few minutes ago.

You have four alternatives to deal with the situation. First, you can always retract your WSPs before running the tests. This is annoying, and it fails when you need it most: Given a tricky bug in your business logic, you usually think about nothing else. This is when you forget to retract your WSPs, which in turn leads to strange behavior, which leads to even more quirks in your mental model of the business logic.

The second solution is to use assembly versioning. If you link to a specific version of your assembly, your outdated version in the GAC will be ignored. This way has the same drawback as the previous one: You will forget it. And even if you have enough discipline to update the assembly version whenever you change your code, you will run into conflicts when your colleagues work on the same assembly. As soon as you check in your code into version control, you have a conflict. It is easy to resolve, but it is annoying.

The third way is to install your class library to the GAC whenever you update it. This works on your local developer machine, but as soon as you start building different releases on a continuous integration machine, you will get conflicts when the different versions overwrite each other in the GAC. Additionally you have to deal with the insufficient robustness of gacutil.exe from the Windows SDK. When you call it too often in a row, it will start throwing errors about insufficient rights. Adding pauses can resolve this, but pauses are what developers hate the most during test-driven development.

So basically you have to find a way to avoid the GAC at all, the fourth alternative. The first step it to separate your code into a SharePoint interface and the actual program logic. The SharePoint interface should be as minimalistic as possible, it should only delegate the calls to the testable program logic. This logic is compiled as a separate class library. Now the SharePoint interface has to be able to find the logic. You cannot use the GAC, since you would run into the previously described problems. A common solution for stand alone applications is to integrate the assembly using ILMerge. This enables separate tests of the logic, while only a single assembly is going to be deployed. This works fine for a single closed program. It breaks down when multiple SharePoint solutions refer to the same assemblies. When a class library is integrated into multiple assemblies, .NET treats the code as separate copies. Therefore all types are multiplied. Accordingly all static variables are multiplied. This confuses even experienced developers.

Now we know that we cannot put the program logic in the GAC, and we cannot integrate it into the SharePoint interface assemblies. Looking at the project settings in Visual Studio, you see that there is a second deployment target, the bin folder of the web application. This has implications for code security. All assemblies in the GAC run with full trust by default. All assemblies in the bin folder are subject to the trust level specified in the web.config. This defaults to wss_medium. This might be sufficient, but depending on your code, you could have to elevate the rights for your assembly. Call the admins of the SharePoint farm your code is going to be deployed to and find an acceptable trust level and CAS policy. On your local machine you can also set the trust level to Full to get the same rights you would have in the GAC.

SharePoint finds assemblies in the bin folder automatically for all code running in the context of the web interface, for example WebParts. But it won’t take long until you notice that it doesn’t look into the bin folder for feature event receivers when activating features in the Visual Studio F5 deployment. The vssphost4.exe simply does not know about the bin folder. It’s the same for all the other SharePoint related non-web processes, such as timer jobs. The solution is to tell SharePoint about the bin folder. When .NET is unable to find an assembly, the event AssemblyResolve in System.AppDomain is fired. You can add a hook and try to load the assembly from the bin folder:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.SharePoint.Administration;

public static class AssemblyResolver
{
  public static void RegisterResolver(SPWebApplication webApplication)
  {    RegisterResolver(webApplication.IisSettings[SPUrlZone.Default].Path.ToString());
  }

  public static void RegisterResolver(string localWebApplicationPath)
  {    AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs args) { return AssemblyResolve(args, localWebApplicationPath); };
  }

  private static Assembly AssemblyResolve(ResolveEventArgs args, string localWebApplicationPath)
  {
    var name = new AssemblyName(args.Name);
    var files = System.IO.Directory.GetFiles(localWebApplicationPath + "/bin");
    return (from file in files 
        where System.IO.Path.GetFileNameWithoutExtension(file) == name.Name 
        select Assembly.LoadFrom(file))
        .FirstOrDefault(assembly => assembly.GetName().FullName == name.FullName);
  }
}

The only thing you need is a reference to the respective web application. In feature event receivers, you can get it via the feature parent object. In item event receivers, you can get it via the parent list of the item and its parent web.

You have to add the assembly resolve logic to the GAC. And you have to add the hook before .NET tries to invoke a function with a reference to a type in the bin folder. This is important since .NET ensures that all types are available before the function is entered, not when the actual line of code is reached. Using proxy classes for event receivers and similar SharePoint classes is a good way to hide all of this from developers.

We developed and implemented the proxy classes at adesso. We might publish the implementation, be it open or closed source, but this is still undecided. If you are interested in this, please leave a comment. I am not the one who decides, but we are actively seeking opinions on this, so you will actually influence the outcome.