Thursday, 27 March 2014

Another Look at Unit Testing Umbraco Surface Controllers (Part 1)

When coming to work with Umbraco as an MVC developer one feature that is immediately familiar and comfortable to work with is surface controllers. Unfortunately, unlike standard MVC controllers, they aren't straightforward to test. In this blog post I'm going to look to start from scratch, uncover the issues and see what options we have to get around this.

Failing test attempt

Starting with a simple example taken from the Umbraco documentation, this surface controller action very simply handles a form post.

The view model:

public class CommentViewModel
{
    [Required]
    public string Name { get; set; }

    [Required]
    public string Email { get; set; }

    [Required]
    [Display(Name = "Enter a comment")]
    public string Comment { get; set; }
}

The view and the form in a partial:

@if (TempData["CustomMessage"] != null)
{
    

@TempData["CustomMessage"].ToString()

} @Html.Partial("_CommentForm", new SurfaceControllerUnitTests.Models.CommentViewModel()) ... @model SurfaceControllerUnitTests.Models.CommentViewModel @using (Html.BeginUmbracoForm("CreateComment", "BlogPostSurface")) { @Html.EditorFor(x => Model) <input type="submit" /> }

And the controller:

public class BlogPostSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
    [HttpPost]
    public ActionResult CreateComment(CommentViewModel model)
    {    
        if (!ModelState.IsValid)
        {
            return CurrentUmbracoPage();
        }

        TempData.Add("CustomMessage", "Thanks for your comment.");

        return RedirectToCurrentUmbracoPage();
    }
}

Based on that we can write this test:

[TestMethod]
public void CreateComment_WithValidComment_RedirectsWithMessage()
{
    // Arrange
    var controller = new BlogPostSurfaceController();
    var model = new CommentViewModel
    {
        Name = "Fred",
        Email = "fred@freddie.com",
        Comment = "Can I test this?",
    };

    // Act
    var result = controller.CreateComment(model);

    // Assert
    Assert.IsNotNull(result);
}

Which... fails, with the exception:

System.ArgumentNullException: Value cannot be null.
Parameter name: umbracoContext
Result StackTrace: 
at Umbraco.Web.Mvc.PluginController..ctor(UmbracoContext umbracoContext)
   at Umbraco.Web.Mvc.SurfaceController..ctor()
   at SurfaceControllerUnitTests.Controllers.BlogPostSurfaceController..ctor()

Avoiding the issue

One way to approach this is really to avoid the issue which I'm blogged about briefly before. To move the logic the controller performs out into a separate class that we reference from the controller, that doesn't have an Umbraco dependency. We can then test that. Leaving behind a controller that's so thin there's really little value in testing it.

Now clearly the controller I already have is pretty thin, but sometimes the logic here that goes into the handler class can get quite involved - a registration form that needs validation, date checks, CAPTCHA checks etc. So I've found this a reasonable approach.

The new handler class looks like this:

public class BlogPostSurfaceControllerCommandHandler
{
    public ModelStateDictionary ModelState { get; set; }

    public TempDataDictionary TempData { get; set; }

    public bool HandleCreateComment(CommentViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return false;
        }

        TempData.Add("CustomMessage", "Thanks for your comment.");
        return true;
    }
}

With the controller becoming like this (or you could dependency inject the handler instance):

public class BlogPostSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
    BlogPostSurfaceControllerCommandHandler _commandHandler;

    public BlogPostSurfaceController()
    {
        _commandHandler = new BlogPostSurfaceControllerCommandHandler();
        _commandHandler.ModelState = ModelState;
        _commandHandler.TempData = TempData;
    }

    [HttpPost]
    public ActionResult CreateComment(CommentViewModel model)
    {
        if (!_commandHandler.HandleCreateComment(model))
        {
            return CurrentUmbracoPage();
        }

        return RedirectToCurrentUmbracoPage();
    }
}

And the test for the handler looks like this - which, as expected, now comes out green.

[TestClass]
public class BlogPostSurfaceControllerCommandHandlerTests
{
    [TestMethod]
    public void CreateComment_WithValidComment_ReturnsTrueWithMessage()
    {
        // Arrange
        var handler = new BlogPostSurfaceControllerCommandHandler();
        handler.ModelState = new ModelStateDictionary();
        handler.TempData = new TempDataDictionary();
        var model = new CommentViewModel
        {
            Name = "Fred",
            Email = "fred@freddie.com",
            Comment = "Can I test this?",
        };

        // Act
        var result = handler.HandleCreateComment(model);

        // Assert
        Assert.IsTrue(result);
        Assert.IsNotNull(handler.TempData["CustomMessage"]);
    }
}

Using Umbraco's test classes

So far have only read about this option - so will come back to it in part 2 of this series of posts..

Wednesday, 26 March 2014

Unit testing Umbraco IPublishedContent with Microsoft Fakes

Update: with more recent versions of Umbraco, there is a way introduced to me by Lars-Erik and documented in his blog post here that allows for testing IPublished content as attempted in this now fairly old post, without the user of MS Fakes. Given the the MS Fakes product still requires the Ultimate version of VS.Net I'd recommend using the technique he describes.

I've recently been working on a package for Umbraco called Umbraco Mapper. It's intention is to support development of MVC based Umbraco applications by providing a simple means of mapping Umbraco content to custom view models. You can read more about it on the GitHub page.

For a few weeks now I've on and off tackled a problem I've found with unit testing part of the functionality. Umbraco content is represented in the front-end of the application by an interface IPublishedContent. What I'm looking to do is to create an instance of IPublishedContent with some known properties, map it to a custom view model class using a method of the mapper, and assert the results are as expected.

First failed attempt... using mocks

In this situation where I have a third party class to instantiate, the first tool I turn to is moq. This is a great little library for unit testing when you have an interface to work with - you can mock an instance that implements this interface and provide known values and functionality for properties and methods. Given those values, you can then action your test and assert the expected outcomes.

Here's a simple mock of IPublished content:

private static IPublishedContent MockIPublishedContent()
{
    var mock = new Mock<IPublishedContent>();
    mock.Setup(x => x.Id).Returns(1000);
    mock.Setup(x => x.Name).Returns("Test content");
    mock.Setup(x => x.CreatorName).Returns("A.N. Editor");
    return mock.Object;
}

And a test that uses that mock:

[TestMethod]
public void UmbracoMapper_MapFromIPublishedContent_MapsNativePropertiesWithMatchingNames()
{
    // Arrange
    var model = new SimpleViewModel();
    var content = MockIPublishedContent();
    var mapper = new UmbracoMapper();

    // Act
    mapper.Map(content, model);

    // Assert
    Assert.AreEqual(1000, model.Id);
    Assert.AreEqual("Test content", model.Name);
}

So far so good, but here I'm only testing properties of IPublishedContent. Of more value in the mapping tool is the mapping of the Umbraco document type properties, which are accessible via a method GetPropertyValue(alias). This makes calls various levels into the Umbraco code base, eventually leading to a null reference exception I believe due to missing a context of some sort.

Now I can add that method to the mock easily enough...

    mock.Setup(x => x.GetPropertyValue(It.IsAny<string>()))
      .Returns((string alias) => MockIPublishedContentProperty(alias));

... but unfortunately this doesn't work, as GetPropertyValue is an extension method that moq is unable to handle. The code compiles, but at runtime you'll get an error and this test will fail.

public void UmbracoMapper_MapFromIPublishedContent_MapsCustomPropertiesWithMatchingNames()
{
    // Arrange
    var model = new SimpleViewModel3();
    var content = MockIPublishedContent();
    var mapper = new UmbracoMapper();

    // Act
    mapper.Map(content, model);

    // Assert
    Assert.AreEqual("This is the body text", model.BodyText);
}

Second failed attempt... using stubs

My next attempt was to create my own class, StubPublishedContent that implements the IPublishedContent interface. Instead of mocking the properties and methods I created stub instantiations of them to just return constant values or simple variations based on inputs.

Of course as I found above, GetPropertyValue(alias) isn't defined on the IPublishedContent interface - but I figured if I just create a method with that signature, given instance methods take precedence over extension methods with the same signature, maybe it'll use my implementation at runtime? Well, no. Confused me for a bit but the citizens of stackoverflow set me straight.

And success... using Microsoft's Fakes

Microsoft Fakes offers a means of replacing certain method calls within any referenced assembly at runtime. To do this you find the assembly that your method is in - in my case umbraco.dll - right-click on the reference and select Add Fakes Assembly.

Once that's complete I could re-write my failing test above like this:

[TestMethod]
public void UmbracoMapper_MapFromIPublishedContent_MapsCustomPropertiesWithMatchingNames()
{
    // Using a shim of umbraco.dll
    using (ShimsContext.Create())
    {
        // Arrange
        var model = new SimpleViewModel3();
        var mapper = new UmbracoMapper();
        var content = new StubPublishedContent();

        // - shim GetPropertyValue (an extension method on IPublishedContent in Umbraco.Web.PublishedContentExtensions)
        Umbraco.Web.Fakes.ShimPublishedContentExtensions.GetPropertyValueIPublishedContentStringBoolean =
            (doc, alias, recursive) =>
            {
                switch (alias)
                {
                    case "bodyText":
                        return "This is the body text";
                    default:
                        return string.Empty;
                }                        
            };

        // Act
        mapper.Map(content, model);

        // Assert
        Assert.AreEqual(1000, model.Id);
        Assert.AreEqual("Test content", model.Name);
        Assert.AreEqual("This is the body text", model.BodyText);
    }
}

I'm replacing the call to GetPropertyValue(alias, recursive) at runtime with my own function, that uses a simple switch statement to return the appropriate document type property value. And at last... a green test!

Monday, 17 March 2014

Async and await with external web resources

Async and await are two keywords that have been available in the C# for a while now. And so whilst probably a bit late to the party thought would share how I've used them for a recent feature, in case they are also new to others.

They allow you to write async code much more easily than was previously possible, which should allow applications to scale better - particularly when there's a need to call out to slow running processes (e.g. external web services), or really anything that is "IO bound" (like a database call.

So for example you could speed up a web request that requires two calls to two long running processes by running them in parallel on different threads and then waiting for both to return. If they both take 1 second your result can return in 1 second instead of 2.

But even with just one long running process in the request, it's still useful, as whilst that process is running the thread serving the request is no longer blocked, and can return to the pool to process other requests. Meaning IIS under load doesn't run out of threads and stop serving content even for simpler or static pages that don't require access to the resource.

Here's the before. I have a controller action that calls up to a service layer to get and parse content from an RSS feed. It ends up at this helper method:

private List<ExternalContentItem> GetItemsFromExternalSource(string url, string category)
 {
    var feedXML = XDocument.Load(url);
    return _rssParser.GetItemsFromXml(feedXML, category).ToList();
 }

And the after. Various long-running methods in .Net 4.5 have been updated to have async versions. XDocument.Load isn't one of them, but if you drop down to the HttpClient you can do this:

private async Task<List<ExternalContentItem>> GetItemsFromExternalSourceAsync(string url, string category)
{
    var httpClient = new HttpClient();
    var response = await httpClient.GetAsync(url).ConfigureAwait(false);
    if (response.StatusCode == HttpStatusCode.OK)
    {
        var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);

        var feedXML = XDocument.Load(stream);
        return _rssParser.GetItemsFromXml(feedXML, category).ToList();
    }

    return new List<ExternalContentItem>();
}

To briefly explain the key points:

  • The async keyword does nothing other than to allow you to use await.
  • Await tells the compiler to convert this into async code, so the thread is returned to the pool and code resumes once the call is complete.
  • Your return type needs to change to be a Task of whatever you actually want back.
  • Your calling code can get the result by calling the .Result property on the task.
  • Those ConfigureAwait(false) calls are apparently necessary to avoid deadlocks. They aren't needed if you are using async code throughout the request, but as I'm just amending this method I'm led to believe they are needed here.
  • One other oddity is exception handling. Any exceptions get aggregated up into a new exception type, which is a bit harder to deal with. That's why I've added that check on the status code rather than using a try/catch in the calling code which I had before.

Note important point that this code won't actually run any faster. It's just that it won't block whilst it's waiting.

For this site, this was probably just an academic exercise - I have caching in place too so these calls are very few and far between. But I think this is becoming more and more of an important technique to be aware of.

With MVC we can now create async controllers and with Entity Framework 6 there will also be asynchronous data access. Which means you can be async all the way up and down, and given that, it'll probably be a question of "why not?" rather than "why?" to use them. I can see in the not too distant future it being just the default way to do things.