CHAPTER 14
One of the real hidden gems when it comes to Nancy is the testing framework provided with it. Nancy was built from the word “go” with the ability to test it in mind. Because of this, the super-duper-happy-path extends not only to developing applications with it, but to testing applications too.
Like everything else you can add to Nancy, you can get the testing framework using NuGet, by installing Nancy.Testing.
In order to take a closer look at how to use the testing framework, add a new class library project to your existing solution. You can create the tests as part of your actual application if you wish, but keeping them in a separate assembly is both recommended and sane.

Figure 31: Adding a new class library project to put our tests in
Once you have added your new class library project, pull up your NuGet Package Manager and add Nancy.Testing to the new project:

Figure 32: Adding the Nancy testing package
While you're in your Package Manager, add the NUnit testing framework (or whichever test framework you prefer to use) to the project. I Use NUnit because I'm a JetBrains ReSharper user, and R# has built-in support for NUnit by default, allowing me to easily manage my tests.
The tests I write in this chapter to demo Nancy's testing abilities will be written using NUnit's syntax. If you use a different test framework, you'll need to alter your tests so that they use the syntax your framework and test runner support.
Add a new class to your test project and add in the following code:
Code Listing 68
using nancybook.modules; using Nancy; using Nancy.Testing; using NUnit.Framework; namespace NancyTesting { class GetTest { [Test] public void SimpleTestOne() { // Arrange var browser = new Browser(with => with.Module(new TestingRoutes())); // Act var response = browser.Get("/"); // Assert Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
} } } |
You'll see that TestingRoutes is in red, and if you run your test at this point, it will fail to build.
Add a project reference to your main Nancy-based application to the testing project, and then in your main Nancy application, add an empty route module called TestingRoutes.cs as follows:
Code Listing 69
using System; using System.Collections.Generic; using System.IO; using Nancy; using Nancy.Responses; namespace nancybook.modules { public class TestingRoutes : NancyModule { } } |
Update your test to include the namespace where your new module lives. In my case, I added using nancybook.modules; to the using clause of my test assembly.
At this point, the TestingRoutes reference should change from red to your default class name color, indicating that it can be referenced.
If you try running your test now, you should find that the test compiles and runs. It still fails, however, as we've not yet implemented the route we’re testing for.

Figure 33: Our first test failing
Go back to your main Nancy application, and update the testing routes module so that it looks like this:
using Nancy; namespace nancybook.modules { public class TestingRoutes : NancyModule { public TestingRoutes() { Get[@"/"] = _ => "Hello World"; } } } |
Run your test again, and this time it should pass.

Figure 34: This time our test passes
Nancy provides you with a Browser object. This object is not an abstraction of the browser in your system; its a custom, self-running headless browser instance that's able to perform many of the things your browser can do, only in isolation.
For example, you can test your post acceptors very easily by asking Nancy to send form values to your routes and examine the response returned. Let's imagine we have a route that allows us to store a name and email address in the database, and we want that route to provide a status code of 200 if the record was saved, 409 if it already existed, or 500 for any other error.
To do this, we'd most likely create three tests as follows:
Code Listing 70
[Test] public void PostTest_Should_Return_200() { // Arrange var browser = new Browser(with => with.Module(new TestingRoutes())); // Act var response = browser.Post("/save/", (with) => { with.HttpRequest(); with.FormValue("Name", "Joe Smith"); with.FormValue("Email", "[email protected]"); }); // Assert Assert.AreEqual(HttpStatusCode.OK, response.StatusCode) } [Test] public void PostTest_Should_Return_409() { // Arrange var browser = new Browser(with => with.Module(new TestingRoutes())); // Act var response = browser.Post("/save/", (with) => { with.HttpRequest(); with.FormValue("Name", "Existing Person"); with.FormValue("Email", "[email protected]"); }); // Assert Assert.AreEqual(HttpStatusCode.Conflict, response.StatusCode); } [Test] public void PostTest_Should_Return_500() { // Arrange var browser = new Browser(with => with.Module(new TestingRoutes())); // Act var response = browser.Post("/save/", (with) => { with.HttpRequest(); with.FormValue("NOTName", "Existing Person"); with.FormValue("NOTEmail", "[email protected]"); }); // Assert Assert.AreEqual(HttpStatusCode.InternalServerError, response.StatusCode); } |
If you run these immediately, you'll see that they fail, as expected.

Figure 35: All our post tests fail
Let's switch back to our testing routes module in the main application and check for our parameters; if they don't exist, we'll return a 500 error.
Code Listing 71
using Nancy; namespace nancybook.modules { public class TestingRoutes : NancyModule { public TestingRoutes() { Get[@"/"] = _ => "Hello World"; Post[@"/save"] = _ => { if (!Request.Form.Name.HasValue || !Request.Form.Email.HasValue) { return 500; } return null; }; } } } |
Rerun your tests, and all being well, the 500 one should pass, but the other two should still fail. You might actually find that the 200 test passes too; even though we returned a null, Nancy will still see that as a successful test and pass a 200 ok by default. If it does, don't worry—for our test purposes, it's perfectly fine.
Let's make our test actually pass the other two tests as expected. Change your route module so that the code now looks like this:
Code Listing 72
using Nancy; namespace nancybook.modules { public class TestingRoutes : NancyModule { public TestingRoutes() { Get[@"/"] = _ => "Hello World"; Post[@"/save"] = _ => { if (!Request.Form.Name.HasValue || !Request.Form.Email.HasValue) { return HttpStatusCode.InternalServerError; } if (Request.Form.Name == "Existing Person" && Request.Form.Email == "[email protected]") { return HttpStatusCode.Conflict; } return HttpStatusCode.OK; }; } } } |
This time, all three tests should pass.
You can also check for other results, such as redirects. Let's imagine that instead of returning a 500, our route module returned a redirect to an error page. We can test for this by changing the test as follows:
Code Listing 73
[Test] public void PostTest_Should_Return_500() { // Arrange var browser = new Browser(with => with.Module(new TestingRoutes())); // Act var response = browser.Post("/save/", (with) => { with.HttpRequest(); with.FormValue("NOTName", "Existing Person"); with.FormValue("NOTEmail", "[email protected]"); }); // Assert response.ShouldHaveRedirectedTo("/posterror"); } |
Running it without changing the route should once again fail. However, we can change the route so that it performs a redirect as follows:
Code Listing 74
using Nancy; namespace nancybook.modules { public class TestingRoutes : NancyModule { public TestingRoutes() { Get[@"/"] = _ => "Hello World"; Post[@"/save"] = _ => { if (!Request.Form.Name.HasValue || !Request.Form.Email.HasValue) { return Response.AsRedirect("/posterror"); } if (Request.Form.Name == "Existing Person" && Request.Form.Email == "[email protected]") { return HttpStatusCode.Conflict; } return HttpStatusCode.OK; }; } } } |
This will once again cause the test to pass.
Nancy's testing features can also check the returned HTML content for given elements, classes, and IDs; in fact, if you can create a CSS3 style selector for it, then you can test for it.
The test classes use CsQuery under the hood for this, and speaking from experience, I've yet to find a selector it's not been able to handle.
Here’s an example from the NancyFX Wiki: if you wanted to submit an incorrect login to a login form, then check that the output contained an HTML element called errorBox, with a certain CSS class, you could use the following test to do this easily:
Code Listing 75
[Test] public void Should_display_error_message_when_error_passed() { // Given var bootstrapper = new DefaultNancyBootstrapper(); var browser = new Browser(bootstrapper); // When var response = browser.Get("/login", (with) => { with.HttpRequest(); with.Query("error", "true"); }); // Then response.Body["#errorBox"] .ShouldExistOnce() .And.ShouldBeOfClass("floatingError") .And.ShouldContain( "invalid", StringComparison.InvariantCultureIgnoreCase); } |
You'll notice in that example that we created the browser using a default Nancy bootstrapper. You can modify this bootstrapper in exactly the same way as you modify your custom bootstrappers in the main application. You can also use it to configure the IoC container to further aid you in your testing efforts.
In this chapter, you saw much more of the super-duper-happy-path. You saw how easy Nancy makes it for you to test your Nancy applications, and how it solves one of the biggest headaches that ALL web developers face: testing the content of the HTML that makes up user interfaces.
Over the last 100 pages, you've discovered (I hope) that Nancy once again brings joy to developing web-based services. When I was discussing this with some of my peers, getting ideas for what to include in the book, one of them said something to me that's stuck with me since I started writing it: "NancyFX? Isn’t that the toolkit that tries to be NodeJS for C#?"
NancyFX is much more, in my opinion, than NodeJS can ever be (although Node does come a very close second). NancyFX represents a framework that has been built by developers who have gone through all the pain that the rest of us have, and have come out of the other side bearing the scars.
The next time you’re browsing around on Twitter, remember to ping a message to @TheCodeJunkie and @GrumpyDev thanking them for making your life and web development efforts so much easier. If you'd like to participate in the project and possibly become a Nancy MVM (Most Valued Minion) then hop over to NancyFX.org and read the docs.
There's so more you can do with the framework—we’ve reached the end of our journey in this book, but your journey with Nancy has just begun.

Figure 36: Enjoy the "Super-Duper-Happy-Path" to web application development.