I’ve been working quite a lot with EPiServer in the last six years or so, and unit testing has always been a pain in the ass (not that I was writing unit tests anyway six years ago, I hardly knew what I was doing back then… can’t believe I actually got paid! :-)). And it still is, even in the latest version.
Luckily there are some EPiServer developers that are either smarter than me, or just have more free time, for example Joel Abrahamson who has created a wrapper project, EPiAbstractions, for wrapping the unmockable EPiServer classes in mockable classes and interfaces. Wonderful!
We started using this in our current project recently, and mostly it works fine. Instead of using EPiServer.DataFactory inside our business classes, we now inject IDataFactoryFacade from EPiAbstractions, and use that instead. Like this:
1 | //Old way |
Simple enough, it let’s us mock the DataFactory with Moq, and we can use an IoC container like StructureMap to inject the the DataFactory class, or we can create a constructor overload that uses DataFactoryFacade.Instance.
The problem
So, with all these nice interfaces that we are able to mock, what is the problem? Well, I was trying to write a test for this method:
1 | public void SetAnmälanTillåten(int verksamhetPageId, bool anmälanTillåten) |
This is a pretty simple method, it gets a page from the DataFactory, sets a property, and saves it again. So we just need to mock GetPage and Save, right? I tried it, and I got this error:
1 | The type initializer for 'EPiServer.Web.PermanentLinkMapStore' threw an exception. |
GetPage was mocked like this (somewhat simplified):
1 | var page = new PageData(); |
So, what is the problem? Well, a look inside EPiServer.dll told me that when calling CreateWriteableClone for the PageData object, it in turn calls CreateWriteableClone for each property in the PropertyDataCollection. When it comes to the PageLink property, it calls PropertyPageReference.CreateWriteableClone:
This doesn’t look so bad, does it? But the problem is in this.GuidValue:
So, unless the field _pageGuid has a non-empty value, it will call PermanentLinkMapStore.Find, and we will get the error. Unfortunately, since the setter won’t let us manually set the value without calling Modified(), which will also make external calls, we can’t use it to set the value.
The solution
System.Reflection to the rescue! With a little helper method for setting the private field, I was able to get the test working:
1 | private static void SetPrivateField<T>( object o, string fieldName, T newValue ) |
And, yay, we have a working test! (Of course, the actual test also had to do a setup for Save, but I’ll leave that as an exercise for the reader.
Finally, there may well be an easier way to do this. If so, I would be most interested to hear of it! Now, I’ll get back to writing unit tests for my project (we still have pretty bad code coverage…). I have a feeling I’ll have to revisit this topic in the future, as more interesting mocking scenarios appear, though.