Sunday, June 17, 2012

Persisting model state when using PRG

I've been working on an ASP.NET MVC application in which we frequently apply the Post/Redirect/Get pattern. One of the direct consequences of applying this pattern is that you often want to persist the model state across redirects, so that you don't lose validation errors, or the values of input fields.

To persist the model state across redirects, we can put TempData to work. The sole purpose of TempData is exactly this; persisting state until the next request.
public ActionResult Index()
{
    ViewData.Model = ...

    if (TempData.ContainsKey("ModelState"))
        ModelState.Merge((ModelStateDictionary)TempData["ModelState"]);

    return View();
}

[HttpPost]        
public ActionResult Update(AddModel inputModel)
{
    if (ModelState.IsValid)
        ...

    TempData["ModelState"] = ModelState;

    return RedirectToAction("Index");
}
So this works, but I found it to be a bit too cumbersome. And so did Davy Brion, he introduced a clean abstraction into the project, smoothing out some of the friction: making use of action filter attributes, we were able to eliminate duplication across controllers, leaving behind an AOP-ish taste.

The SetTempDataModelStateAttribute stores the model state in the TempData dictionary.
public class SetTempDataModelStateAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);         
        filterContext.Controller.TempData["ModelState"] = 
           filterContext.Controller.ViewData.ModelState;
    }
}
While the RestoreModelStateFromTempDataAttribute restores it by pulling it out of TempData again, when it exists.
public class RestoreModelStateFromTempDataAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);
        if (filterContext.Controller.TempData.ContainsKey("ModelState"))
        {
            filterContext.Controller.ViewData.ModelState.Merge(
                (ModelStateDictionary)filterContext.Controller.TempData["ModelState"]);
        }
    }
}
So when we apply these attributes to the example at the beginning of this post, we end up with something like this.
[RestoreModelStateFromTempData]
public ActionResult Index()
{
    ViewData.Model = ...    

    return View();
}

[HttpPost]   
[SetTempDataModelState]     
public ActionResult Update(AddModel inputModel)
{
    if (ModelState.IsValid)
        ...
    
    return RedirectToAction("Index");
}
Very clean. I'm interested to hear how you handle these concerns when using the PRG pattern.

Monday, June 11, 2012

Making my first NancyFx test pass

Like I already said last week, I have been dabbling a bit with NancyFx lately.

This week I took a serious look at testing Nancy modules and Razor views. Due to Nancy's defaults and conventions, it takes a little while to set up Nancy in a test context. Then again, Nancy's granularity makes it simple enough to set up a solid test infrastructure by replacing some of its building blocks.

Like always, I had to go through several iterations to get it right.

A first attempt

The first test I wrote looked something like this. This test simply asserts whether a GET request to the root of my application returns a 200 OK status code.
[TestMethod]
public void root_should_return_response_ok()
{        
    var browser = new Browser(new DefaultNancyBootstrapper());

    var response = browser.Get("/");

    Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}

Assert.AreEqual failed. Expected:<OK>. Actual:<NotFound>.
Telling Nancy which modules to use

I figured out I had to tell Nancy which modules to load. You can do this by using the configurable bootstrapper, which gives you an API to configure parts of Nancy yourself.
var bootstrapper = new ConfigurableBootstrapper(with =>
{
    with.Module<RootModule>();               
});
var browser = new Browser(bootstrapper);
Running this test, I no longer got a NotFound result, but an exception. Making progress.
Test method RootModuleTests.root_should_return_response_ok threw exception: 
System.Exception: ConfigurableBootstrapper Exception ---> 
                  Nancy.RequestExecutionException: Oh noes! ---> 
                  Nancy.ViewEngines.ViewNotFoundException: Unable to locate view 'HomeView'
Currently available view engine extensions: sshtml,html,htm
Locations inspected: HomeView,views/HomeView,views/HomeView,/HomeView,views/Root/HomeView,Root/HomeView
Where is the view engine?

On inspecting the exception, I noticed that the cshtml view engine extension was missing from the list.

To add the view engine, you can just add a reference to the Nancy.ViewEngines.Razor assembly. Nancy will pick up the new engine automatically.
Test method RootModuleTests.root_should_return_response_ok threw exception: 
System.Exception: ConfigurableBootstrapper Exception ---> 
                    Nancy.RequestExecutionException: Oh noes! ---> 
                    Nancy.ViewEngines.ViewNotFoundException: Unable to locate view 'HomeView'
Currently available view engine extensions: sshtml,html,htm,cshtml,vbhtml
Locations inspected: HomeView,views/HomeView,views/HomeView,/HomeView,views/Root/HomeView,Root/HomeView
Locating the views

This time around we get a slightly different exception; the razor view engine extension is available, but Nancy is still having trouble finding the views - which is normal. Nancy is running in a different context now, so the default rootpathprovider will return the wrong path. Implementing another one is children's play.
public class TestRootPathProvider : IRootPathProvider
{    
    public string GetRootPath()
    {
        return "C:\MyProject\";
    }
}
I discovered that you don't even need to use the configurable bootstrapper to override the IRoothPathProvider; Nancy seems to pick up the new implementation by herself.

The hardcoded path will work on your machine, but not on the build server. In my second iteration, I made the implementation smarter by traversing my way through parent directories looking for my views folder - starting from my tests out folder all the way up to my web application folder.
public class TestRootPathProvider : IRootPathProvider
{
    private static string _cachedRootPath;

    public string GetRootPath()
    {
        if (!string.IsNullOrEmpty(_cachedRootPath))
            return _cachedRootPath;

        var currentDirectory = new DirectoryInfo(Environment.CurrentDirectory);
        
        bool rootPathFound = false;            
        while (!rootPathFound)
        {
            var directoriesContainingViewFolder = currentDirectory.GetDirectories(
                      "Views", SearchOption.AllDirectories);
            if (directoriesContainingViewFolder.Any())
            {
                _cachedRootPath = directoriesContainingViewFolder.First().FullName;
                rootPathFound = true;
            }

            currentDirectory = currentDirectory.Parent;
        }

        return _cachedRootPath;
    }
}

Result: Passed
I was happy to find out that this technique works for the MSTest runner, NCrunch and Appharbor.

There is one more way you can go at this though, which works and maybe even is the 'recommended' technique, but also is a lot more cumbersome: use the DeploymentItem attribute to copy the views folder to your tests out folder.

To recapitulate

I hope this post documented some useful techniques to get started testing Nancy's modules and views:
  1. Use the configurable bootstrapper to add your modules
  2. Reference the correct view engine
  3. Implement a rootpathprovider telling Nancy where it can find her views
Only a few days ago, @thecodejunkie made a ticket on GitHub addressing some of these issues. I'm pretty confident we can smooth out these rough edges to make Nancy tests also walk the super-duper-happy-path.

Sunday, June 10, 2012

Book review: The Art of Agile Development

While I have - obviously - read the Agile Manifesto before, and regularly click through to articles on agile, I had never read an extensive work on it. Browsing for a good book, I was advised by a peer to get The Art of Agile Development.

I wholeheartedly believe in the Agile Manifesto, but somewhere along the way Agile - with a capital A - got somewhat of a bad rep. The authors of this book, James Shore and Shane Warden, already predicted this five years ago. Throughout the book, I never had the feeling they were connected to the Agile mob, but just genuinely care about working software.
I fully expect the big consulting companies to start offering Certified Agile Processes and Certified Agile Consultants - for astronomical fees, of course - any day now. Please don't get sucked into that mess.
The book consists of three parts: getting started, practicing XP and mastering agility. Notice the title of the second part, this is a book on agile development using the Extreme Programming methodology. This should be mentioned in the title.
The second part is also the dominant chunk of the book - 271 pages to be precise -, in which 37 agile development practices are covered. These practices are bundled into five categories: thinking, collaborating, releasing, planning and developing. Each practice follows the same recipe: breakdown, questions, results, contraindications, alternatives and further reading. These recipes go into detail, making this book suitable as a reference. As a result, it often also reads as reference material; I found it hard at times to stay attentive. The material is a bit tedious, sometimes a bit repetitive, and while some of the real world experiences are very interesting, the made up stories often read as a children's book. Nonetheless, this doesn't make the information in this book any less valuable. The authors have lived it through and through, and possess a very healthy and pragmatic vision on agile software development.
Ultimately what matters is success, however you define it. The practices, principles, and values are merely guides along the way.  Start by following the practices rigorously. Learn what the the principles mean. Break the rules, experiment, see what works, and learn some more. Share your insights and passion, and learn even more.
I rate this book 4/5. You can get it on Amazon.

Tuesday, June 5, 2012

NancyFx Appharbor builds timing out

I have been working on a petite portfolio site for my girlfriend which is implemented in NancyFx, hosted in ASP.NET. On deploying the project to Appharbor, my builds kept timing out. Since the build log was empty, I turned to Twitter for help. This fine gentleman provided me with a solution.

When you add the Razor view engine to your project using the Nuget package, the postbuild event of your project will be modified to xcopy some assemblies into the bin - which apparently is an Intellisense and precompilation thing.

You do not want to do this in production. Open your project file (or project properties), and change the postbuild event to include a configuration condition.
<PropertyGroup>
    <PostBuildEvent>
      if $(ConfigurationName) == Debug (
        xcopy /s /y "$(SolutionDir)packages\Nancy.Viewengines.Razor.0.11.0\BuildProviders\Nancy.ViewEngines.Razor.BuildProviders.dll" "$(ProjectDir)bin"
        xcopy /s /y "$(SolutionDir)packages\Nancy.Viewengines.Razor.0.11.0\lib\Net40\Nancy.ViewEngines.Razor.dll" "$(ProjectDir)bin"
      )
    </PostBuildEvent>
</PropertyGroup>
I have it on good authority that this will be fixed in the next package.

Sunday, June 3, 2012

The 'everyone should learn to code' dilemma

Back when I was working on software for fire departments, we started thinking about reworking a critical piece of our solution: deployment plans. In a fire department domain, deployment plans help to make a suggestion to the dispatcher about which units should be dispatched to a location when an incident is called in. The suggested composition of units depends on a wide range of variables: availability, response time, ranks, type of incident, required tools, ... , even politics. Originally, people high enough in rank could compose these plans using a decision tree-like UI. However, as it turned out, this UI was insufficient; not all variables and conditions were available. Since this was no custom built tool, we had to work around it by composing incomprehensible decision trees or by tricking the underlying services. When talking about how we could do better, we hit a wall pretty soon. We thought about building our own - but more extensive - UI, and damn, even designing a DSL crossed our minds.
Along the process, I heard that Configuration Complexity Clock ticking, and I couldn't stop myself from thinking that if everyone knew how to code - just some boolean logic and control flow would suffice -, we wouldn't have gotten into this mess. It seemed impossible to build something that would be intuitive, and still fulfill all the requirements; some things just seem to be best expressed in code.

I empathize with the proponents of teaching everyone how to code. On the other hand, I've been watching this movement from a safe distance; there are two sides to the same coin.

Just a few weeks ago, a peer shared one of those horror stories which originate when non-professional developers take matters into their own hands. The IT department of this big company was looking for a piece of software which could standardize the way employees make hardware and support requests. Shopping around, they found the existing products couldn't satisfy their needs, or they would take a considerable cut out of their yearly budget. Eventually, a system administrator who knew just enough about programming to be dangerous stepped up, and rolled his own tool. For over two years, all was good with the world. Maybe the solution wasn't very pretty, and a bit on the slow side, but overall - and most importantly - it got the job done. Now, the original developer has decided he can't be bothered maintaining his brainchild any longer. The peer who told me this story, was also the guy chosen to take over maintenance and feature requests. They assured him that it wouldn't be too much trouble; just a little feature here and there, and maybe the exceptional bug. What he found however, were the things nightmares are made of; a - classic - ASP.NET web application with just one web page, written in VB.NET, containing over 20k LOC, with updatepanels nested five levels deep.

And this is the perfect example of how we have to be very careful with the 'everyone should learn to code' meme. Slinging code is easy, but writing good code is hard, and takes a lot of practice. Software might be eating up the world, but maybe it's for the best we don't turn it into a monstrous glutton.