Within this tutorial, you will learn how to unit test/mock CMS data that is returned from Umbraco. In case you weren't aware, Umbraco ships with a feature called Models Builder. This feature can be used to generate C# classes based on the document types you create within Umbraco. While Models Builder works in a few modes, as you have clicked on an article all about unit testing, I'm assuming you will use Models Builder to generate C# classes and you want to unit test these classes.
The next consideration around unit testing these models is the dynamic nature of the content contained within the CMS. In terms of test creation, it is a bad idea to create unit tests where you make assertions based on hard-coded content. If you go this route and someone changes content within the CMS, your unit tests will automatically start to fail. In terms of a good development experience, you will obviously want to work within a predictable dev-ops flow where your tests don't randomly fail.
This means that when you write tests that rely on data returned from a database you will need to go the mocking route. Mocking in these situations is much better than hardcoding values within your tests, or even trying to connect to your Umbraco database from your test project.
When we talk about mocking in .NET, the obvious framework to turn to is Moq and this is the framework I am using in the code snippets provided below. The other framework to make a quick note of here is NUnit. The code samples below are making use of NUnit. I'm using NUnit as a personal preference, however, there are no restrictions on which test framework you can use with Umbraco
How To Unit Test Umbraco Models Builder
When it comes to thinking about model builder-related tests, for the most part, you will likely want to create tests that validate your view models are returning the correct data from the CMS. A crude example of how a view model might be used to expose data from Umbraco is shown below. NOTE: This code is kept simple to aid in your understanding. I would not structure a view model in production like this, so don't judge!
The code above uses the new .NET 8 primary constructor syntax to pass the CMS data contained within an object created using the Umbraco Model builder called MyDocumentType
. The view model has a public property called Prop
, whose main purpose is to expose some CMS content, the output is a more N-tier architecture style.
Based on this view model, a test that you might want to write is a test that validates that the content is actually exposed correctly through the public Prop
property. This is where a combination of mocking and unit testing comes into play...
Models generated by Umbraco can not be mocked. Each model requires two constructor arguments IPublishedContent
and IPublishedValueFallback
, which can be mocked. This is why within the Init()
method, you will need to initiate the Umbraco models builder type and mock its constructor arguments.
When you want to simulate CMS content, you can not assign data via the model's properties directly. This is because models generated by Umbraco all have private setters.
Due to this limit, if you want to simulate CMS content, you need to follow a pretty convoluted process. For each property that you want the model's builder object to return, you need to create and mock an object of type IPublishedProperty
. With a property object created, you can then associate it with the IPublishedContent
object. Remember, the IPublishedContent
object is the thing you need to pass as a constructor argument when you instantiate your Umrbaco models builder object. Granted, that sounds complex, so let us break it down. First, we define a property and the data we want it to return using this code:
Whenever you want to simulate a CMS property, you need to map the Alias
property and the GetValue(()
method. Alias
needs to relate to the property name, while GetValue(()
is responsible for returning the CMS content you want to simulate.
GetValue()
takes two constructor arguments, culture
and segment
. Personally, when I need to mock Umbraco properties unless I am doing multi-language, I don't really care what values are passed in here. This is why I am using It.IsAny()
for both constructor arguments in the code above. This code means, match regardless of what is passed into GetValue()
, it will return some data, perfect! Finally, within Returns()
you add the content you want to mock!
With the property defined, you now need to associate it with IPublishedContent
, you can do that like this:
One thing to note with the snippet above is that I'm using the value prop
within GetProperty()
. The reason why I'm using a lowercase p
here is that if you look at the generated code within MyDocumentType
for Prop
, you will see something like this:
Within Umbraco itself the property is called prop
however it was converted to uppercase when the C# class was generated. This can be useful to keep in mind if your objects are not returning your mock data correctly. This could also be changed to be more strongly typed if you prefer like this instead:
With that set-up code done, you can write tests like this:
When the code is run, Props
will return the content set within the mock. You have now mocked the Umbraco Models Builder and your CMS content!