In this tutorial, you will learn how and why you should create unit-testable wrappers around any code that renders Umbraco properties using the Umbraco helper. You will learn how to create code that is testable. This post assumes that you are not using the Umbraco model builder to render properties within your website. We will start by discussing the issues using the recommend approach. The default way to render properties defined on a document-type from a page created in Umbraco is to write code that looks similar to this:
Personally, I think there is a lot wrong with this line of code. Let us dissect what I think is wrong.
Not strongly-typed: Umbraco websites are build using C#. One of the main benefits of using C#, compared to JavaScript or PHP, is that it is a strongly-typed language. When building a CMS with a strongly-typed language, we want to take advantage of that. Rendering properties by hardcoding the property name in view is a massive cause of bugs. When someone renames a property in the CMS, you will have no idea what code will have broken in your site. Representing Umbraco document-types using C# pbjects will prevent this issue.
In Umbraco, you can not create strongly-typed models out-of-the-box. Instead, you will have to use a community package. You have several to pick from, including UmbracoMapper and uSiteBuilder.
Not Testable This code uses a static singleton helper to query the CMS. This API requires the HTTPContext object to be set. The code above cannot be mocked and can not be unit tested.
Not Flexible For Upgrades This code is tightly coupled with the version of Umbraco the site uses. If Umbraco released a new API in a later version, you would need to update the whole of your codebase to use it. As an alternative, if you make all property requests via a method within a class that has been injected via construction injection (following SOLID principles) you will have better flexibility. If the API updates, you can change one method to use the new changes and the whole of your website will be updated. This benefit alone can save months of development and refactoring effort down the line.
No Null Checks: If node.GetProperty("contentTitle")
is empty PropertyValue()
will throw an object reference exception. My personal preference when dealing with content is a fail slow approach, if the content is missing an exception should not take the whole page down. In CMS development, I think it's better to render as much of the content of the pages as possible and log/alert major problems in the background.
I'm hoping my arguments have convinced you that wrapping up all calls to GetProperty()
is beneficial to your codebase. If you are still a little unsure about how to implement this type of change, I'll walk you through it. Instead, of using this technique to render a property, let us write some safer code:
The code to get property data can now be written like this:
Just adding a simple static class as an intermediary step that wraps up your dynamic Umbraco calls gives you a lot more flexibility compared to directly using the UmbracoHelper. Using this code, you can pass in any property name and have confidence that your web page won't blow up if a property doesn't exist. If you wanted to build on this further, instead of returning null
you could do some additional logging.
Testable Approach
The new way to render property is a little better, however, it is still not perfect yet. This code isn't testable, which make me sad 🥺. If you want to have a super flexible testable solution, I wouldn't create a static class. Instead, you should access the helper by injecting the class it lives within into the code you want to use via the constructor. In essence, you should use dependency injection to access this helper. To change the code to be more testable, remove the static references and add an interface to the class. On application start-up, you register an inversion of control (IoC) framework like StructureMap, or, SimpleInjector. These frameworks will then deal with the automatic constructor injection for you.
When using an IoC, whenever a class is accessed that has a constructor signature with an interface, the Ioc container will check if a mapping has been registered within its config. A mapping rule will tell the IoC container how to create a new object for that particular type. The IoC framework will then automatically inject that object whenever it sees that type in a constructor. Ioc Is pretty simple, however, it can be a bit daunting for a beginner. The theory is harder than the code. Assuming you have an IoC framework installed, the code above can be refactored like this:
With the interface looking like this:
This type can then be injected via a constructor and the helper method called like this:
You now have a safe way to access Umbraco and your code can be unit tested. Wrapping code to use interfaces is a very powerful technique. Using this code will make your codebase a lot more flexible and safer. The aim of this guide is to try and help people new to Umbraco learn the best ways to architect your project so that updating and maintaining it throughout its lifetime isn't a complete nightmare. By taking the time and wrapping up some Umbraco calls you will de-couple a lot of your system, making it more flexible and testable. Happy Coding 🤘