The other day, I was required during writing a unit test on a Sitecore pipeline to be able to get the value from a method in a base class in the original Sitecore API. In this case it was Sitecore.Mvc.Pipelines.Response.RenderRendering.GenerateCacheKey, and what I needed to do was get the cachekey from the base and then modify it to add additional string values to the key. In doing so I realised that the standard ‘abstract it away’ didn’t really work in its traditional form, since the abstraction would not be aware of ‘base.’. I could pass in that specific method as an argument, but felt that a better approach might be to create a utility repository that would allow me to execute code with an expected return. This meant using a normal Mock of this approach, I could abstract the code in question and simply return what I expect to be the value, without worrying about it (in this case) actually getting the API involved.
The problem
This was similar to the code I was working on where AnonymousRepository was an injected constructor parameter for IAnonymousRepository:
protected override string GenerateKey(Rendering rendering, RenderRenderingArgs args) { string cacheKey = AnonymousRepository.GetResult(() => base.GenerateKey(rendering, args)); // modify the cache key from here }
In my test code I could therefore gain control of the return and pass back a value, and also assert that it was called like this:
// Assign Mock<IAnonymousRepository> anonMock = new Mock<IAnonymousRepository>(); anonMock.Setup(x => x.GetResult(It.IsAny<Func<string>>())).Returns(originalCacheKey); // Act - do something // Assert anonMock.Verify(x => x.GetResult(It.IsAny<Func<string>>()), Times.Once);
It is worth noting at this point that I am using the Moq – It.IsAny. This is due to the fact that I am using a expressions as the lambda. If I created a method I could get to (like using an internal method and exposing it to the unit test assembly via [InternalsVisibleTo(“testassembly”)] then I *believe* I would be able to do a more concrete assertion on the method being called since the call to AnonymousRepository.GetResult(MyMethodName); – in this instance I believe anonMock.Verify(x => x.GetResult(classUnderTest.MyMethodName), Times.Once); would be ok. I am not 100% certain of this though. In this case, there is only 1 abstracted method, so I am ok with it.
The anonymous repository
Here is how I wrote the anonymous repository
/// <summary> /// An anonymous helper that allows results to be drawn from code which is unknown. /// </summary> public interface IAnonymousRepository { /// <summary> /// Gets the result of a function (usually that cannot be tested successfully) /// </summary> /// <returns></returns> T GetResult<T>(Func<T> resultFunc); } public class AnonymousRepository : IAnonymousRepository { public T GetResult<T>(Func<T> resultFunc) { if (resultFunc == null) { throw new ArgumentNullException("resultFunc", "Result function cannot be null"); } return resultFunc(); } }
Conclusion
Whilst this approach utilises a Func, you could equally use Action, Expression etc to achieve similar.
It is worth noting however, I consider this a last resort, and would favour a more solid abstraction over this method due to the relatively transient nature in which the expressions are asserted against.
Hope this helps
Nat