Peter Keating

Developer from the New Forest in the South of England.

Tips from my First Experience with Nancy


Predominantly all the .NET related work I do for Moov2 involves using the ASP.NET MVC framework. The framework is popular with so many resources available online to help with any issues that may be faced. However, for an educational side project I decided to move away from ASP.NET MVC to open my eyes to the alternatives and learn something different and new. Nancy is a lightweight open source framework that I have wanted to play with having caught my eye on a few blogs and a handful of projects on Github. This post isn't going to be about the ins and outs of Nancy, their documentation does a good job to cover that, this post is just a handful of things that either would have been useful to know when starting out or points of interest.

Examples

An excellent way to learn about a framework is to see it in action. The Nancy Github repository contains plenty of demos that handle specific areas of functionality such as authentication, validation & view engines to name a few. Unfortunately the demos don't show how to put everything together to form a project. Luckily I had come across a project by Code52 that used Nancy for a weekly open source project, Ideastrike. The source code is a great reference giving a good picture of how to make it all fit together as a project. Ideastrike contains a test suite for the Nancy modules in the project, even though they used a different testing approach and framework to myself, but it is still a useful resource on how to test Nancy modules.

Serving Static Content

Documentation is provided in the Nancy Github repository on managing static content, however I used an alternative approach I found easier that isn't in the documentation. In the documentation they explain how to define files and directories that represent static content using the Bootstrapper. For some reason I struggled to get this working as expected, and searched for a simpler alternative. Ideastrike had what I was looking for, adding a Web.config to the directory that contains the static content providing instructions to override the default Nancy behaviour, and serve static content. The markup inside the Web.config file is shown below.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <httpHandlers>
      <remove verb="*" path="*"/>
    </httpHandlers>
  </system.web>

  <system.webServer>
    <handlers>
      <remove name="Nancy" />
    </handlers>
  </system.webServer>
</configuration>

Changing default JSON Serializer

By default Nancy uses its own JSON serializer when dealing with JSON in a request or response. The way that the default serializer handles dates wasn't in a human readable format so I seeked to use Json.NET by James Newton-King. A common theme when working with Nancy is that if you want to do something, chances are someone has already been down that path and done it. That was the case in this scenario, on the NancyFX Github account there is a repository that provides a serializer that uses the Json.NET framework to handle JSON.

In order to get this working search for the "Nancy.Serialization.JsonNet" in NuGet package manager and install. You will need to have a custom Bootstrapper in order to get this to work, if you haven't got one setup there is documentation on how to do so in the Nancy Github project. Once you have a custom Bootstrapper, adding the code below will instruct Nancy to use the Json.NET serializer instead of the default.

protected override NancyInternalConfiguration InternalConfiguration
{
    get { return NancyInternalConfiguration.WithOverrides(c => c.Serializers.Insert(0, typeof(JsonNetSerializer))); }
}

Testing Nancy Modules

When developing I always try to follow a test driven development approach. I'm not completely strict on myself but I like to use tests to speed up development by isolating functionality so I don't have to run the whole project to test a piece of functionality behaves as expected. It is important to test the Modules in Nancy are responsible for driving the features in a project. Nancy comes with helpful classes that assist when testing modules, specifically the Browser class that simulates requests to modules that need to be tested. ConfigurableBootstrapper is another class that aids testing by overriding the default behaviour of Nancy so its behavior is suited to testing. IdeaStrike has a nice architecture for putting all this together using an abstract class that can be extended by classes responsible for testing modules. The abstract class configures an instance of ConfigurableBootstrapper for the module that is being tested and also provides useful methods to generate requests to test a modules behaviour. Below is an example of the bare bones that is needed for the abstract class to test a module.

public class CustomRootPathProvider : IRootPathProvider
{
    public string GetRootPath()
    {
        return Path.GetDirectoryName(typeof(Bootstrapper).Assembly.Location);
    }
}

public class ModuleTestBase<TModule> where TModule : NancyModule
{
    protected ConfigurableBootstrapper Bootstrapper;
    protected Browser Browser;
    protected BrowserResponse Response;

    public ModuleTestBase()
    {
        Bootstrapper = new ConfigurableBootstrapper(with =>
        {
            with.Module<TModule>();
            with.Dependencies(
                Mock<IDependancy>().Object,
                Mock<IAnotherDependancy>().Object
            );
            with.NancyEngine<NancyEngine>();
            with.ViewFactory(AutoMoqer.GetMock<IViewFactory>().Object);
            with.RootPathProvider<CustomRootPathProvider>();
        });
    }

    protected void Get(string path, Action<BrowserContext> browserContext = null)
    {
        Browser = new Browser(Bootstrapper);
        Response = Browser.Get(path, browserContext);
    }

    protected void Post(string path, Action<BrowserContext> browserContext = null)
    {
        Browser = new Browser(Bootstrapper);
        Response = Browser.Post(path, browserContext);
    }

    public void SetView(string path, HtmlResponse htmlResponse)
    {
        AutoMoqer.GetMock<IViewFactory>().Setup(v => v.RenderView(path, It.IsAny<object>(), It.IsAny<ViewLocationContext>()))
                   .Returns(htmlResponse);
    }
}

Below is an example of a testing a module using the abstract class above.

public class HomeModuleTests : ModuleTestBase<HomeModule>
{
    [Test]
    public void Default_CanBeFound()
    {
        SetView("Home/Index", new HtmlResponse());
        Get("/");

        Assert.That(Response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
    }
 }

The SetView method is required when testing module routes that will return HTML, without the response will return a NotAccepted status code. The test is simulating a request to the homepage route and checks that the response returned has the status code of OK.

Summary

I have been wanting to have a go with the Nancy framework for quite a while and it has been a pleasure to use. What made the experience for me whenever I was looking to do something a little different it was catered for by the framework, all I needed to do was a quick Google search or rummage through the source code and I would find the solution I was looking for. What is really encouraging about Nancy is how actively developed it is by lots of people contributing with changes being committed everyday. The Github repository is loaded with content that could keep you occupied with healthy discussions, good documentation and the well structured source code with demos. Finally if you can't find what your looking for with Nancy, chances are someone has contributed an extension that is available, just like the Json.NET serialization package. If you still can't find what your looking for, then you have a great opportunity to contribute your own extension, as mentioned in the introduction Nancy has no barriers that get in the way of customisation.

I've thoroughly enjoyed my experience with developing the Nancy framework and hope to be using it in client work in the near future. I hope some of the points mentioned in this blog post are of some use, if you have any to share with me then get in touch by email or send me a tweet.

Back to Posts

-->