Archive
Sitecore MVC new Ninject Controller Factory – clean version
In this post we will have revisit my last blog post, using Ninject with Sitecore MVC. In this new approach we will simplify the code to do the exactly same thing as in the last post LINK.
So what we want is to be able to inject Concrete implementation in the our Sitecore Controller.
We will do this by creating a ninjectc controller factory and forwarding to the default Sitecore controller factory, so our implementation is used when Sitecore creates it’s controller. By doing it this way all standard Sitecore MVC functionality will still work, but we now have the possibility to inject our concrete implementation on creation time of the controllers.
First we need a Factory for creating the Ninject kernel
public class NinjectKernelFactory
{
public IKernel Create()
{
return LoadAssembliesIntoKernel(new StandardKernel());
}
private IKernel LoadAssembliesIntoKernel(IKernel kernel)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
kernel.Load(assembly);
}
catch (Exception)
{
//Empty Catch used because ninject have problem
//with loading some of the Sitecore MVC assemblies.
// Method .ToString()
}
}
return kernel;
}
}
With that in place we can create the the NinjectControllerFactory
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel _kernel;
public NinjectControllerFactory(IKernel kernel)
{
_kernel = kernel;
}
public override void ReleaseController(IController controller)
{
_kernel.Release(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return (IController)_kernel.Get(controllerType);
}
}
]
All that is left is now for binding it all together in a new InitilizeContollerFactory
public class InitializeNinjectControllerFactory
{
public virtual void Process(PipelineArgs args)
{
SetControllerFactory(args);
}
protected virtual void SetControllerFactory(PipelineArgs args)
{
NinjectKernelFactory kernelFactory = new NinjectKernelFactory();
NinjectControllerFactory ninjectControllerFactory = new NinjectControllerFactory(kernelFactory.Create());
SitecoreControllerFactory sitecoreControllerFactory = new SitecoreControllerFactory(ninjectControllerFactory);
ControllerBuilder.Current.SetControllerFactory(sitecoreControllerFactory);
}
}
And off course we need to swap the originale InitlizeControllerFactory with our new one.
Sitecore default :
</pre> <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc"/>
Replaced with this:
<processor type="SitecoreNinjectModule.InitializeNinjectControllerFactory, SitecoreNinjectModule"/>
Now Lets try it our I have created a really simple example first an Item with a controller rendering on.
And the sourcode for the controller
public class StoryController : SitecoreController
{
private ITestInterface _testInterface;
public StoryController(ITestInterface testInterface)
{
_testInterface = testInterface;
}
public ActionResult From()
{
ViewBag.Froms = _testInterface.Get();
return View();
}
}
And now to the View Code
And our RazerView
@model dynamic <h2>From vIew</h2> @ViewBag.Froms
And a simple TestInterface and TestClass
public interface ITestInterface
{
string Get();
}
public class TestClass : ITestInterface
{
public string Get()
{
return "Hallo From Test";
}
}
And the output of it all
There we are a much nicer and cleaner solution presented then I came up with in my last blog post and without breaking any Sitecore functionalit, and off course you can still unit test the controller.
Sitecore MVC Ninject controller
This is last post in the series of three. As promised in my last post we will inject a repository through the construct in the controller. To do this we will use the Ninject as our IOC container, This will be a very quick walkthrough.
Also we to make it a bit easier to find controller that needs to have class’ injected we will create a derived class from the controller class’,
public class NinjectController : Controller
{}
Some of the assmeblies in sitecore mvc solution gives and error when trying to reflect upon them so there are a couple of catch’s in the code to handle some minor bugs.
Next we will create our controller factory wrapping the Sitecores standard factory. This new Ninject controller factory will for the controller that should be loaded is of type nijectcontroller instantiate the corresponding concrete type of the constructers parameters.. Also this class loads all the ninject binding. Maybe it’s not so nice that it has multiple responsibility but it is left out for an exercise to refactor the code so it conforms to SRP 🙂 .
public class NinjectInitializeControllerFacotry
{
public virtual void Process(PipelineArgs args)
{
this.SetControllerFactory(args);
}
protected virtual void SetControllerFactory(PipelineArgs args)
{
NinjectControllerFactory controllerFactory = new NinjectControllerFactory(ControllerBuilder.Current.GetControllerFactory());
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
}
and the controller factory it self
public class NinjectControllerFactory : SitecoreControllerFactory
{
public NinjectControllerFactory(IControllerFactory innerFactory) : base(innerFactory)
{
}
public override IController CreateController(RequestContext requestContext, string controllerName)
{
Assert.ArgumentNotNull(requestContext, "requestContext");
Assert.ArgumentNotNull(controllerName, "controllerName");
try
{
IController controller = ICreateController(requestContext, controllerName);
return controller;
}
catch (Exception exception)
{
if (!MvcSettings.DetailedErrorOnMissingController)
{
throw;
}
return new ErrorHandlingController(controllerName, exception);
}
}
public override void ReleaseController(IController controller)
{
var disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
private IController ICreateController(RequestContext requestContext, string controllerName)
{
string fullControllerName = string.Format("{0}Controller", controllerName);
Type ninjectController = FindSpecificNinjectController(fullControllerName);
if (ninjectController != null)
{
return (IController)Kernel.Get(ninjectController);
}
return base.CreateController(requestContext, controllerName);
}
private Type FindSpecificNinjectController(string controllerName)
{
IEnumerable<Type> ninjectControllers = GetNinjectControllers();
if(ninjectControllers.Any())
{
Type selectedNincjectController = ninjectControllers.FirstOrDefault(controller => controller.Name == controllerName);
if (selectedNincjectController != null)
return selectedNincjectController;
}
return null;
}
private IEnumerable<Type> GetNinjectControllers()
{
List<Type> types = new List<Type>();
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
types.AddRange(FindDerivedTypes(assembly, typeof(NinjectController)));
return types;
}
private IEnumerable<Type> FindDerivedTypes(Assembly assembly, Type baseType)
{
try
{
return assembly.GetTypes().Where(t => baseType.IsAssignableFrom(t));
}
catch (Exception)
{
return new List<Type>();
}
}
private static IKernel _kernel;
private IKernel Kernel
{
get
{
if (_kernel == null)
CreateKernel();
return _kernel;
}
}
protected void CreateKernel()
{
_kernel = new StandardKernel();
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (HasNinjectModules(assembly))
_kernel.Load(assembly);
}
}
public static bool HasNinjectModules(Assembly assembly)
{
Type baseType = typeof (NinjectModule);
try
{
return assembly.GetTypes().Any(t => baseType.IsAssignableFrom(t));
}
catch (Exception)
{
return false;
}
}
}
So all that is left is to overwrite the standard controller factory.
<initialize> <processor type="SitecorePresentation.SitecoreMVCRoutes,SitecorePresentation"> <mvcIgnoreHomePage>false</mvcIgnoreHomePage> </processor> <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeGlobalFilters, Sitecore.Mvc"/> <processor type="SitecoreMVCNinjectExtension.NinjectInitializeControllerFacotry, SitecoreMVCNinjectExtension"/> <!--<processor type="Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc"/>--> <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeRoutes, Sitecore.Mvc"/> </initialize>
Next we can rewrite the controller from the last episode to conform to the new ninjectcontroller
public class NinjectItemController : NinjectController
{
private readonly IChildItemRepository _childItemRepository;
public NinjectItemController(IChildItemRepository childItemRepository)
{
_childItemRepository = childItemRepository;
}
public JsonResult Index(string id)
{
return Json(CreateObject(_childItemRepository.Get(id)), JsonRequestBehavior.AllowGet);
}
private IEnumerable<object> CreateObject(IEnumerable<Item> items)
{
List<object> objectList = new List<object>();
foreach (Item item in items)
objectList.Add(new { item.Name });
return objectList;
}
}
The repositories from the last post looks the samen but are listed below.
</pre>
public interface IChildItemRepository
{
IEnumerable<Item> Get(string parentItemId);
}
<pre>
</pre>
public class ChildItemRepository : IChildItemRepository
{
public virtual IEnumerable<Item> Get(string parentItemId)
{
ID parentId;
if(ID.TryParse(parentItemId,out parentId))
return GetChildNodes(parentId);
return new List<Item>();
}
private IEnumerable<Item> GetChildNodes(ID parentId)
{
Item parentItem = Sitecore.Context.Database.GetItem(parentId);
return parentItem.GetChildren();
}
}
Now to ninject magic the ninject module that factory are loading bindings from.
public class ChildItemRepositoryNinjectModule : NinjectModule
{
public override void Load()
{
Bind<IChildItemRepository>().To<ChildItemRepository>();
}
}
And of course now we can rewrite out unittest to test the new controller.
public class ItemControllerTest
{
[Test]
public void ItemControllWithNoIdShouldReturnEmptyList()
{
IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
childItemRepositoryStub.Get(Arg.Any<string>()).Returns(new List<Item>());
NinjectItemController controller = new NinjectItemController(childItemRepositoryStub);
JsonResult result = controller.Index(string.Empty);
Assert.That(result.Data,Is.Empty);
}
[Test]
public void ItemControllWithInvalideIDShouldReturnEmptyList()
{
IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
childItemRepositoryStub.Get(Arg.Any<string>()).Returns(new List<Item>());
NinjectItemController controller = new NinjectItemController(childItemRepositoryStub);
JsonResult result = controller.Index("invalidID");
Assert.That(result.Data, Is.Empty);
}
[Test]
public void ItemControllWithValidIdShouldReturnJsonListWithItemNames()
{
IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
List<Item> childList = new List<Item>();
childList.Add(new ItemStub("stub-a"));
childList.Add(new ItemStub("stub-b"));
childList.Add(new ItemStub("stub-c"));
childList.Add(new ItemStub("stub-d"));
Guid itemGuid = Guid.NewGuid();
childItemRepositoryStub.Get(itemGuid.ToString()).Returns(childList);
NinjectItemController controller = new NinjectItemController(childItemRepositoryStub);
JsonResult result = controller.Index(itemGuid.ToString());
List<object>resultData = (List<object>) result.Data; ;
Assert.AreEqual(4,resultData.Count());
Assert.AreEqual(resultData[0].ToString(),"{ Name = stub-a }");
Assert.AreEqual(resultData[1].ToString(), "{ Name = stub-b }");
Assert.AreEqual(resultData[2].ToString(), "{ Name = stub-c }");
Assert.AreEqual(resultData[3].ToString(), "{ Name = stub-d }");
}
}
The import thing about the rewrite of the test are the ugly SET on the itemctroller is removed as the repository is passed in as contructor argument.The result from the test
As a last service the image below shows the file structure in visual studio .
Thats all, now we can create easy to test controllers using a repository pattern.
Unit testing our MVC Controller for Sitecore
Is this post and as promised, we will unit test the controller we created in the last post.
So we will rewrite the controller a tiny little bit, so we can inject a child item repository, ie. creating a stub for the repository. Since Sitecore as default doesn’t ship with ControllerFactory where it is possible use dependency inject we will rewrite the controller instead, so it has a default repository it uses when Sitecore is instantiating the controller, and one it uses when testing, or more precisely you can pass one in to Repository property. The updated controller code is listed below. In my next post we will extend Sitecore so we can inject class through the contructor with dependency Inject using Ninject.
public class ItemController : Controller
{
private IChildItemRepository _childItemRepository;
public JsonResult Index(string id)
{
return Json(CreateObject(ChildItemRepository.Get(id)), JsonRequestBehavior.AllowGet);
}
public IChildItemRepository ChildItemRepository
{
get { return _childItemRepository ?? (_childItemRepository = new ChildItemRepository()); }
set { _childItemRepository = value; }
}
private IEnumerable<object> CreateObject(IEnumerable<Item> items)
{
List<object> objectList = new List<object>();
foreach (Item item in items)
objectList.Add(new { item.Name });
return objectList;
}
}
Now the three test cases
1. No id supplied the controller should return an empty result.
2. Supplied with an invalide Guid the controller should return an empty result.
3. Supplied with a valid guid, the controller should return a JSon result with the item names for all child items placed under the node with specified Guid,
Now for the Unit test class, as a mocking framework I’ve used NSubstitute. I’ve used the ItemStub I did in the this post Unit testing with Sitecore Item. The code for this test Item is also as a service listet below.
public class ItemStub : Item
{
public ItemStub(string itemName)
: base(ID.NewID, new ItemData(new ItemDefinition(ID.NewID, itemName, ID.NewID, ID.NewID),Sitecore.Globalization.Language.Invariant, new Sitecore.Data.Version(1), new FieldList()), new Database("dummy"))
{
}
}
[TestFixture]
public class ItemControllerTest
{
[Test]
public void ItemControllWithNoIdShouldReturnEmptyList()
{
IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
childItemRepositoryStub.Get(Arg.Any<string>()).Returns(new List<Item>());
ItemController controller = new ItemController();
controller.ChildItemRepository = childItemRepositoryStub;
JsonResult result = controller.Index(string.Empty);
Assert.That(result.Data,Is.Empty);
}
[Test]
public void ItemControllWithInvalideIDShouldReturnEmptyList()
{
IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
childItemRepositoryStub.Get(Arg.Any<string>()).Returns(new List<Item>());
ItemController controller = new ItemController();
controller.ChildItemRepository = childItemRepositoryStub;
JsonResult result = controller.Index("invalidID");
Assert.That(result.Data, Is.Empty);
}
[Test]
public void ItemControllWithValidIdShouldReturnJsonListWithItemNames()
{
IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
List<Item> childList = new List<Item>();
childList.Add(new ItemStub("stub-a"));
childList.Add(new ItemStub("stub-b"));
childList.Add(new ItemStub("stub-c"));
childList.Add(new ItemStub("stub-d"));
Guid itemGuid = Guid.NewGuid();
childItemRepositoryStub.Get(itemGuid.ToString()).Returns(childList);
ItemController controller = new ItemController();
controller.ChildItemRepository = childItemRepositoryStub;
JsonResult result = controller.Index(itemGuid.ToString());
List<object>resultData = (List<object>) result.Data; ;
Assert.AreEqual(4,resultData.Count());
Assert.AreEqual(resultData[0].ToString(),"{ Name = stub-a }");
Assert.AreEqual(resultData[1].ToString(), "{ Name = stub-b }");
Assert.AreEqual(resultData[2].ToString(), "{ Name = stub-c }");
Assert.AreEqual(resultData[3].ToString(), "{ Name = stub-d }");
}
And a screenshot of the test status.
I know that there probably exists better ways to test json results, but it is out of scope for this post, and yes if it should be “clean tests” each test should only contain one assert, but is left for as an exercise for you :).
In the next post we will extend Sitecore so that we can create Controllers through dependency injection, with Ninject as an IoC container. With this solution we don’t need the ugly Set in the Repository Property.
Unit testing Sitecore with Typemock Isolater.
In my last post I gave a stub example for a Sitecore Item. With the stub item you could test functionality for retrieving field data, which greatly limits the functionality you can test. So in this post I will have a look at the more advanced mocking framework Isolator from Typemock. With the Isolator framework it is possible to intercept calls for methods that normally wouldn’t be possible, for example static methods. How may this help us? Since Sitecore hasn’t made it any easier to do unit testing with a great deal of static classes and methods alike, you will be able to mock or stub them out with the Isolator framework. The following is a simple example for testing functionality for Item.Children, ie. Sitecores Childlist.
Here we have a simple piece of code that, given a specific Template ID, adds an item to a list and returns the list.
public class SectionFactory
{
public static IEnumerable<Section> CreateCollection(Item columnItem)
{
ID sectionTemplateId = new ID(Constants.Templates.Section);
ChildList children = columnItem.Children;
List<Item> sections = new List<Section>();
foreach (Item child in children)
{
if (child.TemplateID == sectionTemplateId)
sections.Add(new Section(child))
}
return sections;
}
}
Since getting the children for a Item invokes more than a few static methods and classes it is impossible to stub the functionality using most mocking frameworks. But with the Isolator Framework it is possible.
[TestFixture]
public class SectionFactoryTest
{
[Test]
public void SectionFactoryNoChildrenReturnEmpyList()
{
Item stub = Isolate.Fake.Instance<Item>();
ChildList childlist = Isolate.Fake.Instance<ChildList>();
Isolate.WhenCalled(() => stub.Children).WillReturn(childlist);
IEnumerable<Section> sectionList = SectionFactory.CreateCollection(stub);
Assert.AreEqual(new List<Section>(), sectionList);
}
[Test]
public void SectionFactoryOneChildReturnEmpyList()
{
Item stub = Isolate.Fake.Instance<Item>();
Item child = Isolate.Fake.Instance<Item>();
ChildList childlist = Isolate.Fake.Instance<ChildList>();
ID sectionTemplate = new ID(Constants.Templates.Section);
Isolate.WhenCalled(() => child.TemplateID).WillReturn(sectionTemplate);
Isolate.WhenCalled(() => childlist).WillReturnCollectionValuesOf(new List<Item>(){child});
Isolate.WhenCalled(() => stub.Children).WillReturn(childlist);
ID link = new ID(Constants.Fields.Section.Links);
Isolate.WhenCalled(() => LinkRepository.GetCollection(child, link)).WillReturn(null);
IEnumerable<Section> sectionList = SectionFactory.CreateCollection(stub);
Section section = sectionList.FirstOrDefault();
Assert.That(section, Is.Not.Null);
}
[Test]
public void SectionFactoryOneValidSectionChildChildReturnEmpyList()
{
Item stub = Isolate.Fake.Instance<Item>();
Item sectionChild = Isolate.Fake.Instance<Item>();
Item child = Isolate.Fake.Instance<Item>();
ChildList childlist = Isolate.Fake.Instance<ChildList>();
ID sectionTemplate = new ID(Constants.Templates.Section);
Isolate.WhenCalled(() => sectionChild.TemplateID).WillReturn(sectionTemplate);
Isolate.WhenCalled(() => child.TemplateID).WillReturn(null);
Isolate.WhenCalled(() => childlist).WillReturnCollectionValuesOf(new List<Item>() { child, sectionChild });
Isolate.WhenCalled(() => stub.Children).WillReturn(childlist);
ID link = new ID(Constants.Fields.Section.Links);
Isolate.WhenCalled(() => LinkRepository.GetCollection(child, link)).WillReturn(null);
IEnumerable<Section> sectionList = SectionFactory.CreateCollection(stub);
Assert.AreEqual(1,sectionList.Count());
}
}
So with the above test we managed to get a 100% test coverage for the code.
And with the Isolator Framework we could even mock out the Sitecore.Context. The Isolater framework is must a have if you want to do unit testing with Sitecore.
Unit testing with Sitecore Item
public class NavigationTitleFactory
{
public static string Create(Item item)
{
bool showInNavigation = item.GetCheckBoxField(new ID(Constants.Fields.Navigation.Show));
if (!showInNavigation)
{
return string.Empty;
}
string navigationTitle = item.GetStringField(new ID(Constants.Fields.Navigation.Title));
if (string.IsNullOrEmpty(navigationTitle))
navigationTitle = item.Name;
return navigationTitle;
}
}
public class TestItem : Item
{
public TestItem(FieldList fieldList,string itemName="dummy")
: base(new ID(new Guid()), new ItemData(new ItemDefinition(new ID(new Guid()), itemName, new ID(new Guid()), new ID(new Guid())), Language.Invariant, new Sitecore.Data.Version(1), fieldList), new Database("web"))
{
}
}
- An Item with show in navigation not checked – it should return an empty string.
- An item with show in navigation checked but no navigation titel should return the Name of the Item.
- An item with show in navigation checked and with a navigation title, it should return the value of the navigation title.
The test code i listed below should give 100% test coverage of the code.
[TestFixture]
public class NavigationTitleFactoryTest
{
[Test]
public void CreateItemWithShowInMenuFalseShouldReturnEmptyString()
{
FieldList stubFieldList = new FieldList();
stubFieldList.Add(new ID(Constants.Fields.Navigation.Show),"0");
stubFieldList.Add(new ID(Constants.Fields.Navigation.Title),"NavigationTitle");
Item stub = new TestItem(stubFieldList);
string navigationTitle = NavigationTitleFactory.Create(stub);
Assert.IsNullOrEmpty(navigationTitle);
}
[Test]
public void CreateItemWithShowInMenuTrueNoNavigationTitleShouldReturnItemName()
{
FieldList stubFieldList = new FieldList();
stubFieldList.Add(new ID(Constants.Fields.Navigation.Show), "1");
stubFieldList.Add(new ID(Constants.Fields.Navigation.Title), "");
Item stub = new TestItem(stubFieldList,"myItemName");
string navigationTitle = NavigationTitleFactory.Create(stub);
Assert.AreEqual("myItemName", navigationTitle);
}
[Test]
public void CreateItemWithShowInMenuTrueShouldReturnItemNavigationTitle()
{
FieldList stubFieldList = new FieldList();
stubFieldList.Add(new ID(Constants.Fields.Navigation.Show), "1");
stubFieldList.Add(new ID(Constants.Fields.Navigation.Title), "NavigationTitle");
Item stub = new TestItem(stubFieldList);
string navigationTitle = NavigationTitleFactory.Create(stub);
Assert.AreEqual("NavigationTitle",navigationTitle);
}
}
Allstair Deneys blog on unit testing with
Sitecore using a custom test runnner can be found here.How to change config files to work with Sitecore without a “context” here. By Mike Edwards





