Sunday, December 16, 2012

Addressing the challenges of TDD in Sharepoint – Decoupling the consumer and implementation


The SharePoint Service Locator is a reusable component from the MS Patterns & Practices that provides a simple implementation of the service locator pattern. You can use it in your own SharePoint applications to decouple the consumers of an interface from the implementations of that interface. Instead of creating an object by invoking the constructor of a class, you request an object with a specified interface from the service locator. The service implementation can now be replaced or updated without altering the consuming class implementation.
The usage of service locator makes it easy to dependencies that are scattered throughout various projects and solutions. These dependencies often make it challenging to maintain code over time—if you modify one class, you must recompile every project that references that class. This also makes unit testing code in isolation much more complicated. In short, decoupling your code from specific types makes your code more modular, easier to manage, and easier to test.
You can use the RegsiterTypeMapping method of the ServiceLocatorConfig instance to register the mappings to the service locator.
var serviceLocator = SharePointServiceLocator.GetCurrent();
var mappingsConfig = serviceLocator.GetInstance<IServiceLocatorConfig>();
mappingsConfig.RegisterTypeMapping<IMyService, MyService>();
You can retrieve the interface implementation later in your code using the GetInstance method of the ServiceLocatorConfig instance.
serviceLocator.GetInstance<IMyService>();
Normally you register the instances during the feature activating event and you need to remove the registered mappings from the service locator during feature deactivating event using the remove mappings method.
var serviceLocator = SharePointServiceLocator.GetCurrent();
var mappingsConfig = serviceLocator.GetInstance<IServiceLocatorConfig>();
mappingsConfig.RemoveTypeMapping<IMyService>(null);

Unit testing challenge – Replacing the ServiceLocator in the test context
The service locator from the P&P uses the sharepoint object model to keep store of the mappings and other properties. To successfully unit test the code that uses the service locator, you need to either use a wrapper object for the service locator or use the ActivatingServiceLocator object instead of the ServiceLocator in the test code.
To replace the service locator instance, you need to replace the actual service locator with the ActivatingServiceLocator instance as given below.
var activatingServiceLocator = new ActivatingServiceLocator();
SharePointServiceLocator.ReplaceCurrentServiceLocator(activatingServiceLocator);
const string type1 = "Type1";
const string type2 = "Type2";

activatingServiceLocator.RegisterTypeMapping<INotRegisteredType, NotRegisteredType1>(type1);
activatingServiceLocator.RegisterTypeMapping<INotRegisteredType, NotRegisteredType1>(type2);

var notRegisteredType = SpServiceLocator.GetInstance<INotRegisteredType>(type1);
Assert.IsInstanceOfType(notRegisteredType, typeof(NotRegisteredType1));
SharePointServiceLocator.Reset();

2 comments:

Michael Clifford said...

I noticed you have posts on this, as well as the SharePoint emulator. Have you run into this issue?

http://stackoverflow.com/questions/23483212/how-do-i-use-sharepoint-emulator-with-sharepoint-service-locator

When my team and I try to retrieve the replaced/current service locator within an enabled emulation scope, we get an error that the SPFarm was not found. We also have not had any success in shimming the SPFarm object.

If you've seen this, how were you able to resolve it?

Michael Clifford said...

I noticed you have posts on this, as well as the SharePoint emulator. Have you run into this issue?

http://stackoverflow.com/questions/23483212/how-do-i-use-sharepoint-emulator-with-sharepoint-service-locator

When my team and I try to retrieve the replaced/current service locator within an enabled emulation scope, we get an error that the SPFarm was not found. We also have not had any success in shimming the SPFarm object.

If you've seen this, how were you able to resolve it?