Building better Umbraco sites, using MVC, strong typing and unit testing - Part 1

Most developers when they first start with Umbraco will generally build a site with the logic in the views. As soon as you start building more complicated sites they rapidly become more difficult to build and maintain.

At Gibe we build Umbraco sites in a different way. In these blog posts I'm going to explain how we build sites and the reasoning behind using this structure. Then go in depth into how we actually implement each of the different areas. Because it's so wide ranging I've broken the post down into 4 parts:

  • Part 1: Why use MVC, strong typing and unit testing
  • Part 2: How to develop an MVC Umbraco site
  • Part 3: Using Ditto to implement strong typing
  • Part 4: Unit testing Umbraco sites

Why MVC?

MVC stands for Model-View-Controller, which is a pattern that separates your site's model from it's business logic and views. By putting logic into controllers and having views that are purely concerned with layout. We're then using Umbraco as the model.

Although Umbraco is built on ASP.NET MVC the default way you would implement sites isn't following the MVC pattern. However they have put the tools in place to allow you to implement your site in a MVC manner. This has several benefits:

Maintainability

This is the most obvious advantage, by having code in your views you are making it more difficult to debug and update your site. When code is in a controller it’s a lot easier to read your code, and if there are issues they will show up during compile time not at run time.

Code reuse

By moving code into controllers or even better a set of services the controllers use you make it easier to reuse components between sites. For example, if you include a blog on most of your sites you could create a blog service that is used by the controller. The service could include methods such as MostRecentBlogPosts which will get the 5 most recent blog posts or BlogPost which will get a single blog post. Instead of having a partial/macro in your Umbraco site you can now have the code in a separate class library that you can share between projects.

Unit testing

By moving the logic outside of the view you are now able to test any logic that is in the controller, but more on that later.

Why strongly typed?

I’ll start by explaining what I mean by strongly typing. This is where you pass a custom model to your view instead of the default, usually something like IPublishedContent.  So in your views where you used to have something like:

 @Model.GetPropertyValue<string>("BodyContent") 

You would use:

@Model.BodyContent

To make this simpler for ourselves, we use a library called Ditto, this takes a lot of the legwork out without compromising on flexibility. More details on this will be in part 3.

There are many benefits to strongly typing, especially when you get into more complex content:

Maintainability

As a simple example, let’s say we wanted to include the 5 most recent blog posts on the homepage. The code for that might look something like:

@foreach(var blogpost in Model.Content.AncestorsOrSelf(1).Descendants("blogPost").OrderByDescending(b => b.PublishDate).Take(5)) {
<article>
<h2>@blogpost.GetPropertyValue<string>("blogTitle")</h2>
<p>@blogpost.GetPropertyValue<string>("summary")</p>
</article>
}

If we moved that outside of the view the view would look like this:

@foreach(var blogpost in Model.RecentBlogPosts) {
  <article>
    <h2>@blogpost.Title</h2>
    <p>@blogpost.Summary</p>
  </article>
}

Isn’t that clearer to read and maintain.

What you’re now doing is relying on your Controller to put together a model for the particular view with everything setup and configured for the particular page.

Splitting development

This makes splitting development between different disciplines easier, as a front-end developer can work on the views and know exactly what data will be available. While a back end developer can construct the model and populate it in the controllers.

Compile time errors & Intellisense

Moving the logic to the controllers and using strong typed models means that errors will more often show up during compilation and errors will show up in Visual Studio, and as an added bonus Intellisense will also work within your views.

Why unit testing?

Moving your logic out of your views and strongly typing is great, but to make sure everything is working correctly you really need to do unit testing. Fortunately, both of the above makes unit testing possible. Some of the benefits of unit testing are:

Maintenance

Yes maintenance again, you may start noticing a theme! One of the great benefits of unit testing is that now you can refactor your code, or add new features and be confident that you haven’t broken anything.

Testing your code

This is pretty obvious but unit testing will mean your code is tested before you run it in the website.

Improving code quality

I can unequivocally state that unit testing will make your code better. It forces you to change the way you structure your code better. For code to be unit tested it has to be written with unit testing in mind. This forces you to follow several practices that will make your code better. For starters you really have to write your code within the Single Responsibility Principal (SRP), and implement Inversion of Control (IoC) /Dependency Injection (DI). This is exciting stuff, when you get into it you'll find your code is more flexible. Now you'll be able to make substantial changes without big changes to the code base as it decouples your classes, allowing them to work independently of one another.

Documenting your code

We use our tests as documentation for our code. Say we have a class called BlogPostService and we wanted to test the IEnumberable<BlogPost> MostRecentBlogPosts() method. We could have tests like these:

MostRecentPosts_returns_5_most_recent_posts_in_reverse_data_order
MostRecentPosts_returns_all_posts_when_less_than_5_posts
MostRecentPosts_returns_empty_enumerable_when_0_posts

Just reading the names of these tests makes it clear how the MostRecentBlogPosts() method will work in different scenarios.

Next up...

In part 2 I'll be discussing how to build a site in a true MVC way and get all your logic out of your views.

About the Author

Steve Temple, Technical Director and co-founder of Gibe

Steve is Gibe's technical director and super brain behind the development of our major projects. With over 27 years of commercial experience, Steve is an expert in .NET, Umbraco and Microsoft technologies. Steve is also an Umbraco Certified Master and Microsoft MCSD