Mocking UmbracoHelper and using it with Dependency Injection

The Umbraco APIs are full of examples where the developers have made tough design choices. The UmbracoHelper is the one, from my own experience at least, in which the reasoning behind the design is not immediately obvious.

In many cases, the way the UmbracoHelper was designed is a blessing - enabling seamless use in Razor views and providing convenient self-documentation to the most important Umbraco APIs for heavy IntelliSense users. However, once you start using UmbracoHelper in your own controllers and services, and then start trying to write unit tests for that code and using dependency injection, you may be tempted to view reliance on this ubiquitous static class as a hinderance. The way to decouple your code from this dependency is hard to find through IntelliSense or through documentation.

But don't despair just yet, there is a little-known trick which makes unit testing code that uses the UmbracoHelper APIs both convenient and laughably easy - without endless fiddling trying mock UmbracoContext in your test fixtures.

And there's no need spend your precious time trying to re-implement UmbracoHelper in your own services, especially because UmbracoHelper was written by people who know what they are doing, and hidden in those method calls are often many layers of caching which are hard to get right unless you know Umbraco inside-out.

The trick: UmbracoHelper uses Interface Segregation

My pain with UmbracoHelper came from my expectation that there would be an IUmbracoHelper interface, which I could depend on in my controllers and services and easily mock in my tests.

But what I didn't know is that since 2014 there's been a ton of interfaces which themselves come together in the full UmbracoHelper. Using one interface for the entire class wouldn't be ideal, because there are just so many methods in there, but instead your code can just depend on the interfaces which it actually uses, enabling you to mock out those dependencies with well-known techniques and mocking libraries. Here they are in full:

  • ITagQuery
  • ITypedPublishedContentQuery
  • IDynamicPublishedContentQuery
  • IUmbracoComponentRenderer
  • MembershipHelper
  • UrlProvider
  • IDataTypeService
  • ICultureDictionary

Now this isn't interface segregation by any strict definition, because the class UmbracoHelper doesn't inherit from these directly. Rather, UmbracoHelper exposes these bits of it's API as object properties. Here follows a code example which will make the use of this technique a little clearer.

Writing decoupled code which uses UmbracoHelper

Here's a code snippet from one of my projects. It uses TypedContentAtXPath, a method from UmbracoHelper, to get every content node with a specific document type, and then uses Ditto to convert those IPublishedContent objects into my own model POCOs.

The special thing about this code is that it doesn't even know UmbracoHelper exists. It depends instead on that interface ITypedPublishedContentQuery which defines the bit of the API that it actually uses.

Now, how easy is it to instantiate this class, passing in the required dependency? As I've said above - laughably easy. Here's the relevant line from the AutoFac startup configuration for this same project. UmbracoHelper just exposes, as properties, classes which inherit from these interfaces - in this case ITypedPublishedContentQuery.

If you're not familiar with AutoFac, that's fine, it's not necessary. AutoFac just does the job of passing dependencies into my constructors for me.

You could just as easily instantiate your this same class like this:

Conclusion

After trying unsuccessfully to decouple code from the UmbracoHelper in a graceful way in many projects, this discovery was a real "eureka" moment for me. According to Shannon, not all features of the API are yet available through these interfaces, but most of the work I'm doing with UmbracoHelper was available with the APIs provided in ITypedPublishedContentQuery, making this interface become a close friend to my Umbraco codebases.

One more caveat is the necessity to refactor your code to depend on these interfaces, instead of UmbracoHelper directly or your own home-grown interfaces - but hey - who doesn't like refactoring?

If you've found this article useful, or if you have any feedback, I invite you to get in touch with me on twitter @glcheetham. Thanks for the your time and I hope you enjoyed the read :-)