Test Driving Nancy
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Horsdal, Christian
Nancy is a lightweight .NET web framework. It provides an easy to use, to-the-point alternative to the most commonly used .NET web frameworks. Nancy does not try to be everything to everyone. But it does try to be the super-duper-happy path to web development on .NET and Mono. Come get introduced to Nancy, and judge for yourself, as I build a small Nancy application TDD-style and along the way demonstrate core Nancy features as well as a few slightly more advanced features such as content negotiation.
 Hello, so let's get started. My name is Christian. My contact stuff is there. This is the only slide that's going to be there, so we're switching over to Visual Studio very soon. So it'll be code, code, code. But I'm going to just sort of briefly introduce what I'm going to talk about. So I'm going to talk about the Nancy web framework, which is a.NET-based web framework inspired by the Sinatra web framework, which is a Ruby web framework. Sinatra is probably the thing that you might use in Rubyland if you find that Rails is too heavy. So that's also the thing with Nancy. It's very lightweight. It's very, just tries to make it easy for you to work with. And there are not too many concepts there. So we'll actually cover quite a few of them just within this hour. It's meant to be easy to work with and not get in your way. And you sort of have the freedom to do what you like to do, which is all things that I really like. That also means that Nancy is quite code focused. So that's why I'm showing it off in Visual Studio instead of OK, whatever. Instead of doing it in a lot of slides, because, well, I think in this case at least, code speaks louder than slides. So I'll wait with the slides and over to Visual Studio. So I've cheated a little bit, not too much, I hope, but a little bit. So I've set up a project and a test project as well. So in Nancy today, we are going to build just sort of a little back end for a to-do application. And I've just stolen the Arch typical to-do MVC application done in Backbone.js. So it's a single page application where you can sort of hammer in to-dos. And you can check them off. And that's all of it. And that meant to demonstrate the JavaScript side. We're not going to look at the JavaScript at all. We're just going to look at how could you set up a back end for this, because it's something that throws back some JSON, and it expects to get some other JSON back and forth them. So that's what we're going to build. So what I've set up here is, well, this up here was just a Nancy, no, sorry, an empty HPNet application, just straight out of the box. And then I've added a couple of Nougat packages to it. I've added Nancy itself. And then I've added Nancy.hosting.aspnet. So Nancy tries to or succeeds in actually separating your application from what you want to run it on top of. So there's a Nancy framework itself that's not dependent on ASPNet or anything like that. So to actually run your Nancy application, you also need to have a hosting. One of the hosting features, one of the hosting options, is a spinette. And that's just pretty easy to work with. You can just F5 in Visual Studio, and you can put it on IAS, or you could put it in Azure, or on AppHab, or wherever you want. But you could also go with doing self-hosting, just as Web API also does now. So you could just run your little Nancy application from within a Windows service or a WPF application or whatever. So if you have a WPF application deployed across an enterprise, and you want a backdoor into that, if you want to be like the NSA, but just within your own organization, so you want to be able to look inside what everybody's doing with your application, you just have Nancy and their self-hosted, and then you have HTTP endpoints. And you can just go there and look at stuff, well, except for firewalls, maybe. But if you control those two, right. So those are the Nougat packages I have in this, just Nancy itself and the HPNet hosting. Then in this project down here, I also have a couple of Nougats installed, and there's also some test code in there already that I'll sort of use to drive out what I'm going to code up. And the Nougats I have in this project are, well, just X-Unit. That could be anything. Just because I like X-Unit. And then the Nancy dot testing in Nougat. So probably already sort of becoming clear that Nancy uses Nougat a lot to distribute the code. So you're very much, you generally don't really go to the Nancy site to bring down Nancy. You just use Nougat packages, and then they have sort of different parts of Nancy you can install as Nougat packages. Which is also sort of one of the lightweight parts of Nancy. It's very modular. You can sort of change everything and pull stuff out and put something else in. So also if you want to have a ViewEngine in there with Razor, for instance, and Nancy comes out of the box with another ViewEngine called Super Simple ViewEngine. But if you want Razor, then you can just install a Nougat. Nancy dot ViewEngine is just Razor. And then you have that. Nancy out of the box, as we'll see, comes with an IOC container for dependency injection. But you can also change that around. And oh, I don't want to use tiny IOC as Nancy does out of the box. I want to use Autofac or whatever, or Castle. Then you just install a Nougat, and the Nougat changes things around, and that's it. So that's very much the experience with Nancy for a lot of things. So I've also set up a couple of other things that we're going to need. First of all, this is our domain model, our very large and very intelligent domain model, which is a to-do. And it's just straight up the same properties as the single-page front end is going to have in its JSON representation of the to-do. So this is just really driven by how that backbone application works. I also want to be able to store these to-dos. It's a back end. I want to be able to store it. So I defined this little interface for a data store. And so you can get back all the to-dos, and you can try to add them, or you can try to remove them, or you can try to update one of them. Pretty simple stuff, and not really too interesting. And then I've implemented that against MongoDB and I have a Mongo instance running back here. You don't have to be able to read all that stuff. It's just there's a MongoDB running here. And so the talk is not about Mongo, so I'm not going to show off the code in there. Right. So let's get started, actually. So I'll start off with this test for what I'm going to call the home module. I'll just bring that in there, and move the mouse. Yes, now you can see the code. That's good. And usually there's something red here, because that refers to a class that doesn't exist. But let me just explain what's going on here, because I want to show Nancy from the testing perspective, partly because the testing support is so cool. So I think we should focus on that, too. So what Nancy does testing, that NuGet package I mentioned before, it brings us, among other things, is this browser object, or this browser type. So I can use a browser, and that's going to be my system under test. And that is supposed to be sort of my virtual browser. I want to say that don't think so living in them, and web driver, and GUI testing, and stuff like that. This is just an object that knows a bit about Nancy, so it can give what for Nancy looks like in HTTP request. It can give that to Nancy, and then they'll flow through the Nancy stack, but only the Nancy part. Not the actual HTTP and network stack and all that. And actually not the HPNet hosting part either. It just goes straight to Nancy, gives that what looks like an HTTP request for Nancy. Nancy doesn't know that it didn't come from a real browser, but just comes from here. So it's all in process, and it's fast enough. And you don't have all the trying to find the right element, and click in that or moving mouse, or anything like that. So this is, in my opinion, much more suited for most tests. I will just do the request through Nancy, reach your application code, or our application code, and that code won't be able to tell the difference either. So you get sort of a very nice outside view on your application by testing through this, but without all the obstacles of testing through the UI, which is horrible. Also what the browser allows you to do. It has an API that is, well, it's HTTP. And that's where exposing stuff for HTTP. So we want to be able to see that. So I can say, my system under test, I want to do a get towards slash. And the get is an HTTP get. And I'm doing it to the root of my application. So these paths are obviously relative to the host. I get the actual or the response back. And there are different stuff on the response. You can get to the body and expect what's in the body of the response. Or as I do here, you can get to all the headers. And just for ease of access, you can easily get to the status code. And for this one, I'm just checking that it didn't blow up and it returned a 200 OK. So it's not too much. So first of all, we need this home module. Oh yeah, might actually also explain this funky bit of syntax here. So when Nancy starts up, it wants to make it easy for you to register your application code with the framework. So it tries to discover where is your application code and then just auto-register that. So you don't have to do a whole lot of configuration. So we can rely on that from test as well, actually. Or we can be a bit more specific, which I do like to be in the tests. And I can just say, OK, for this test, I want a browser browser working against Nancy. But Nancy, with only this module that I'm going to explicitly tell you what it is. Just a bit, I'll talk about what a module is. So it doesn't see all of our application. It just sees the home module because that's what I actually want to test against. And you can put more in here or whatever. But I think this makes it quite explicit what the scope of this test actually is. So I like that. OK, so we don't have a home module. So we will add a home module. Can I remember? Yes, and it's just a good old C-sharp class. Home module, yep. Seems like I can spell. Get rid of those. So we have a public class here. And now to make this a Nancy module, I make it inherit. Well, Nancy module. No big surprise. And so Nancy modules are really at the core when you work with Nancy. This is where you actually code up your actual application. So what you do in a Nancy module is that you set up the routes that you want your application to be able to respond to. And you set up the handlers that you want to use for responding to that. So in our case, we need to be able to do a get against slash. So we tell it that here in the constructor. So this is telling Nancy that, OK, set up a route at slash and make that accept the HTTP method get. And we also want to be able to do something there. And what, damn keyboard. There we go. And we'll just give it a lambda expression telling what we actually want to do. And the stuff we want to do is we want to return. So if I was strictly just trying to make my test pass, I could just return HTTP status code dot OK here, because that's what I'll show you then. But we're going to be a little bit more loose here. And I'm going to say that I want to return a view. And the view I want to return is just index.htm, as far as I remember. So that refers by convention to a file called index.htm, obviously. But that can be placed in a few places. And one of them, Nancy by convention, will look in your views folder. You can alter these conventions if you will. You can just set up your own, or you can run with Nancy's. And Nancy is pretty nice about error messages as well. So if it doesn't find a view that you're trying to reference, it'll actually tell you all the places it looked, which is pretty nice. It's like, oh, that's why. So you can decide if you want to move it, or you want to put it into, or if you want to set up your own convention, of course. Yeah, it looks like in views index.htm. And that's just a whole bunch of some HTML. And that basically just loads up that backbone application that we saw before. So along with the JavaScript, I and CSS and stuff I have in here in content, that's the backbone part. So we're not going to worry about that. I just stole that off GitHub. Right. So let's see if it compiles, first of all. It did. And let's go back to our test. And let's see if that runs. And it did. Yay! Hello, world in Nancy. And we got to see Nancy modules. Right. That's a success. So that's not quite enough. But now we are able to return that HTML page that will load up the backbone single page application. That's actually fine. You can actually work with that. But it's just all the calls to the back end fails. You won't see that in the UI, so perceptive. But if you look in Fittler or something, it'll be quite obvious. So we can't actually do anything yet. So as I said, we want to build a back end for this. So driving out the next bit a little bit apart here. So I've just put a little bit of setup here in my constructor for these tests, because I want to reuse it across more of the test functions. You see, again, I'm instantiating a browser here. Setting up a module. Now I want to test against the to-dos module. The to-dos module is the one that actually going to handle all the requests from our single page application. Then we also want to set up something more. I want to set up a dependency, specifically used in this test. So as I said, at its core and out of the box, NINC gives you an IOC container, a version of control container, and it wants you to use dependency injection. So everybody familiar with dependency injection? Yeah, good. And here I just set up a dependency specifically for this test, and I'm setting up a fake data store. And that's just a mock of the I data store that we looked at before, which I'm doing with fake it easy. Again, that's not really the case. You could do it with your favorite mocking framework or by hand or whatever. Set up the dependency. That's fine. And then we have a little bit of test data here. OK. So I want to test that when doing a get to slash to-dos. Well, it's just what I do here. I want the correct number of to-dos to be returned. So I set up my mock here to, well, first of all, this is just creating a list of to-dos. Then I tell my mock's data store that whenever anybody calls get all, just return that list of to-dos. Then I do a get. And by the way, I say that I would like to have the response be application JSON. So this is going to be used in JavaScript, right? So I want JSON back. I don't give me HTML or I don't give me anything else. I want application JSON. So you can also see it during the request. This is really close to HTTP or close. It looks close to HTTP. We're using the method here. We're saying what path. And then here we can set up headers and stuff like that. Say which type of request. Do more around the request. So you can really do a lot of things from the tests here, which is nice. Well, since I'm asking to get application JSON, I'm expecting that I can deserialize the response, the body of the response as JSON. And then I can just see, is it the length that I expected? So that's all well and fine. Again, this won't compile because we don't have a to-dos module. So we will create a to-dos module. to-dos module. There we go. Again, get rid of that. And obviously, this is, again, a Nancy module just before. And like before, we are going to set stuff up in the constructor. So this time, actually, everything that's going to be in here is going to be under this last to-dos part of the application because that's where we want to get the to-dos back, and that's where we want to post new ones and stuff like that. So, OK. There we go. Go away, please. So we just tell the base class, the Nancy module, that everything in here, go away already, should be relative to to-dos. I guess that should be fine like that. So now we just sort of namespace what we're doing in here. And we don't do the get again. And it's just going to be to slash. I guess then I maybe not, I probably don't need the trailing slash up there to-dos. And we want to do something here again. And what we want to do here is we want to retrieve all the to-dos that are stored in our data store right now. So I'm just sort of going to expect or going to assume for a minute that I have a data store and that I can say get all to that. And then I will actually just return that straight as it is. And I need a data store. So I'll take a dependency on the data store. Data store. There we go. And what's it telling me? Return type is void return type of what? Is void. Just compare maybe it tells me something more. My constructor is returning. Yeah. Yeah. Thank you. Yes, that's better. Yeah, yeah, exactly. My constructor was returning that's true. And I meant the lambda to return, of course. Thank you. So yeah, so this is just returning the list of to-dos from my data store. And it's just returning that back into Nancy. So as you see, the lambdas that we give here to get, for example, that are going to handle our request, they can do quite different things. And Nancy will just pick it up and do what you want. So here I'm just returning an innumerable of my domain objects. And that's fine. It's going to be fine. And over here in the home module, I am using Nancy to locate a view and we're returning that. That's quite different things. I could also just return a string, hello world. That would be the body of the string. And Nancy would assume that it's text HTML as content type. Or I could return just a status code. OK, or 404, or whatever I wanted. And again, so Nancy will just take these different types and make them all into a Nancy response. And be able to output that. And when you don't specify a whole lot like we don't do here, Nancy will just sort of assume a few things. For example, it will assume that it's OK. So the status code will be set to OK. And actually Nancy will also, in this case, it will assume that, OK, you're returning some objects. You probably want to put them into the body of your request, or sorry, your response. And you want to say 200 OK as the status code. And when I serialize these objects into the body of the response, you probably want to respect those other tests. You probably want to respect that accept header that the client set. So this should be serialized into Intugation, because that's what the client asked for. OK, so crossing my fingers and seeing if this now passes. It did. Oh, yeah. It looks like three tests, just because I'm running it three times with different amounts. So zero to do, one to do, and 42 to do, because you always need 42 in there. And then just to demonstrate, I have this test down here. It's almost the exact same thing. The difference is here. Now I'm asking for XML, because I still love the 90s. And I expect to be able to deserialize that as XML. And let's try to run that. And that passes as well. So thank you, Nancy. Nancy did the content negotiation for me there, looked at the accept header, and then just serialized based on the accept header. So that's nice. Out of the box. OK, so now our single-player test is done. Now our single-page to-do application is able to get the to-dos that are already on the server side and it'll display those. We can't do anything with them yet. So let's move on to, we'll find for time. True. So we also want to be able to create new to-dos on the client side. So a bit of setup of the mark I have. And then that will be a post. So again, this is the browser object that we look at. Until now, we've used it with Git. But we can also use the post method of HTTP, do it to the same place, and we say this time we just serialize the body that we want to post. We serialize that as JSON. So again, this is setting up a fake HTTP request and HTTP post, and it's putting a body onto that post. And the answer from that, we want that to be created and we want the body in the answer or in the response to contain the to-do that we just posted. OK. Right, so this should fail at the moment. We haven't done that code, right? So that fails as expected. That's nice. It's set. See down here, the status code was not found. No big surprise. We haven't set up anything to handle post to that endpoint. Back over here. And so it's just more of the same, really. I do that. And you can do this with all the methods on HTTP. So if you have the time, at least, we'll show this for put and delete, and it's really more of the same stuff. But you can also do patch and hit and options and stuff. You can set that up. Or you can leave stuff like options and hit out, and then Nancy will just handle that for you. So in the post here, well, we want to do slightly more than just one liner. So first of all, let me just open up a scope. And first of all, we want to get a hold of a new to-do. So that's the to-do that is being posted into us. So we are going to ask Nancy to try to create a to-do for us based on the request. So model binding in Nancy is done like this. In our case, we have the to-do in the body. So Nancy will look in the body here and deserialize that into a to-do if it can. It could also look other places like form values if I remember correctly. I'm pretty sure it will. So once we have that, we just work with our data store again. If, let's say, if not data store.try. And I've just made it so it returns false if it wasn't able to put it in there. And it's our new to-do. We want to put in there. Eh, come on. And so if it's not able to do that, we will return something. And in this case, we'll just return an htp status code. And it will be bad request. And otherwise, we'll assume that it is added. So how much did I assert on over here? I asserted that it was created and that it's, oh, yeah. I think I said something wrong here. I think I said that I was asserting that to-do was in the response. I'm actually not. I'm just asserting that we tried to put it into the fake data store. So inside of this method, we're just messing about with the fake. I've hidden that because it's sort of ugly. So we will actually just return an htp status code here as well of created. Oh, you see, you have all the status codes just named out here, so you can get to them easily. And you just need that. Let's see if it compiles. And it seems to compile. And go back, run this. Right, there we go. So actually, once again, the bind here sort of respects what's on the htp-p request. So in our test here, we explicitly set that using this helpful method that we wanted the body to be JSON. So not only does that serialize my to-do object there, the a to-do, as JSON, but it also sets the htp-hattice to indicate that the body is JSON. So the code we have over in the module would also work if we serialized into XML and then posted that up and set up the content header server correctly, the content type correctly. So again, Nancy respects this content negotiation part of things, and we'll just pick up. So out of the box, Nancy can handle JSON and XML in this way. If you want to do something more, it's really just a matter of implementing two different interfaces, one for the ingoing part for data binding and one for the outgoing part, the serialization to responses. Each of those interfaces have two method needs. And then you can just put that somewhere in your assembly and Nancy will scan the assembly, find the implementation of the interfaces, and they'll just hook into this again. So if you went over to Service Stack maybe and grabbed hold of their implementation of protocol buffers, because that's a very efficient protocol, and you plug that right into those two Nancy interfaces, which is quite easy. It's about 50 lines of code combined those two, and that's even including namespaces and empty lines and stuff like that. Then Nancy will pick that up, and you can do the same here. You can put a protocol buffers serialized to dos into the body of a post and set the content type. Nancy will pick that up. These are a slide for you. It's fine. So it's just another example. It's really easy to just hook into the Nancy engine and just make stuff still happen. And stuff like that sets up a nice separation of concerns as well, because this is sort of my business logic here. I want to pull out the to do wherever it was in the request, and then I want to put it in my database. I don't want to mug about with the exact format here. It's really nice that that's just an infrastructure thing that Nancy handles for me. Nice. OK. Looking at time, that's fine. OK. Yeah. So I'm just checking that I don't want to allow saving the same to do twice. We'll skip that. OK. So as I said, this is just really all the same. We want to delete something, and we want to do that based on the ID of the to do. So every to do has an ID. And again, because this is how the backbone example expects things to be, it wants to do an HTTP delete to do slash the ID of the to do that it wants to delete. So I'll do that, and then I'll ask my fake if we try to remove to do number one from the data store. I'm saying that must have happened. This is just an assertion with the to do, and then I'll do the to do. This is just an assertion with fake it easy. So to no big surprise at this point, I think this is just more of the same. There we go. And so we just say delete. And we say, so well, it's slightly different. We say slash, and then we want to grab hold of the ID. So we do this like that. And then actually now we want to, now I'm going to give sort of the argument for our lambdas a proper name because now I'm going to use it for something. That's why I just call it underscore up there. That underscore is a valid name of an identifier. I like to use it when I'm not actually using that variable for anything because it just sort of visually disappears. So that indicates to me that I don't care about this right now. Whereas when I actually use it, I want to give it a name that I can read. So we need the ID out of there. So I'll just go into the parameters here and I'll just say ID. So parameters here is a Nancy type or has a Nancy type, the type dynamic dictionary. So that's a dynamic object to no surprise. And one of the things you can do with that is that you can just use dot notation to grab hold of whatever you're capturing up here. So what you put in here as the path that I want to handle with this handler, it can be a regular expression. So and you can have different part captured now. And this is really just to capture anything behind the slash to do. But I could write out a regular expression and saying that I only want numbers there and I could go on and I could say I can have another slash and then another part of the path. So I can grab different parts. Anything you can do with a regular expression, knock yourself out. And the parts that you name, you can grab just out of here like that. So, well, yeah, depending on whether you like a dynamic or not, this is really cool. At least it's easy. I do like it for stuff like that, but I also sort of do like to move to keep the dynamic parts sort of right here in the handler, then take the things out and then sort of in business logic behind. I want to just be in the typed part of C sharp. That's a personal opinion. You can have another one, of course. So we will try again to delete, try, remove, and that takes an ID. So it should be fine. And, well, let's do it more or less as before. So if we aren't able to remove it, we will say that that's because it wasn't found. Not found. So that's a 404. And otherwise, we will return and, well, okay, I guess would be fine here. And go compile again. Let's see if that runs. And that, yeah. So we can inspect the to-dos. We can post new ones and we can delete them. I am going to skip number five here, which is doing the same thing, but for put. And it's just really a handler. Very similar to what we always see here. We'll just use data binding like up here to pull out the to-do of the request. And then I will try to store that and return static codes depending on what happened. We will just skip that because I think you get the picture. So the last thing actually we want to sort of do here is, well, we want to start using that Mongo database that I have back here. We haven't used that at all up till now because we are just running against a fake data store. So actually everything we have done up till now would work if I didn't have this class in the solution at all, my actual implementation of the data store. Because as long as I tell the tests, here is a fake implementation of the data store. Well, that's fine for Nancy. You don't need to have an actual implementation of the data store. So you can test drive for a while there and use that also to drive out the interface for your dependencies and then go implement them for real once you are all ready for that so you don't have to guess the interface. Anyway, we want to hook this up. So go to this test here. So this time around we are just first of all making sure that there's no sort of old data in our Mongo database so we can connect to the database that I have a local host at the default port and I just drop it. So I have some empty data to work on. And then I set up a browser again. This time I don't sort of inline do the configuration of it. This time I want this to be an integration test. So I want to run off the bootstrap that I'm going to create in a little bit and that is going to be the bootstrap that the application is actually going to run on. And then we just have a test that posts a to do as JSON. And then once it's finished doing that it does a get to the same place again and says oh I want it as JSON. It tries to deserialize the body of the response from all of this. So this is two calls changed together. So this is nice if you sort of want to test a scenario. Of course this can also get out of hand if you have really long scenarios so you have to use judgment there. You actually have to think even though it is easy. But this is fine just doing two first posting and then getting. And the response here is the response from the last one. That would get out to no big surprise. And then I just assert that I get back what I expected there. That I get back the one that I just posted. So and since I'm going to run on sort of the real bootstrap where there's an integration test that sees that I can store it back to the Mongo database and retrieve it from the Mongo database as well. So just sort of a smoke test that everything is connected correctly. So I don't have a bootstrap yet. And this is the part of the code where I'm going to cheat a little bit. So I'm just going to show all files for a little bit. And then I'm going to grab a bootstrap that I already have here. And we're going to look at that. So until now we've just used Nancy in the default configuration. We haven't even taken advantage of the fact that we have installed the ASP.NET hosting because we haven't run it on a web server yet. We've just run it from the tests. So but if you want to do, when you need to do to configure something for yourself, you do that in the bootstrap. You just have to have a class that implements the I-Nancy bootstrap, I believe, as the interface. And typically the easiest way to do that is actually inheriting from the default Nancy bootstrap. So up until now we would have just run actually off of the default Nancy bootstrap, which well, set up all the defaults. But I want to do a little bit more now. I want to do a little bit configuration of the IFC container. First I just call base. And one of the things that it will do up here is it will auto register everything that it can sort of find. So actually if our Mongo data store, so the implementation of the I data store, if that didn't take any arguments, if that was just something that was coming up, I wouldn't be able, I wouldn't have to do anything here. Nancy would be able to just pick up that I have an implementation of the I data store, which one of my modules needs, and it doesn't take any arguments for its constructor, so well, it's fine. We'll just new that up and assume that that is the, that is the dependency. But since I need to pass in a connection string, I sort of want to take more control. I probably don't want to hard code that either, but demo code. So I just knew up the one connection here to the data store and registered that with the container. And just to demonstrate, there are sort of various parts that you can get into here and set up all kinds of different things within Nancy. But oftentimes you don't actually have to do a whole lot here because Nancy is really good at auto discovering things. Like I said before, if you want to implement your own serializer, fine, just to let Nancy discover it by itself. But if for whatever reason you want to take control of how that's instantiated or something like that, again, you can do that in the bootstrap and you can set up the body deserializers that you want here. So very modular, very much on the conventional configuration side, but it does allow you to go in and change everything basically within the framework itself. Right. So now we have a bootstrap per. We have the Mongo database running. We go back to our test here. Let's see if it compiles at least, it seems to do. And then let's just run this integration just and we can maybe, oh, yeah, it's doing something with the Mongo database. You don't have to read that, but you can just see that something is going on at least and seems to be done. And it actually seems to have succeeded as well. So why don't we actually try to, well, let's compile so we should be able to, this is how the application looked before the session started. If you went to the last, we just had Nancy's very nice custom 404 page there. Oh, and you can see up here, it's a Nancy application. So that's, yeah, so anybody recognize her? It's Nancy Sinatra seen from the site. So let's see what happens if I go there again. So this is, well, you can't really see that this is, well, you can see that there actually. This is just exactly how this to do MVC would look running against anything, of course. You're just seeing the JavaScript here, which is why I'd rather see it from the test perspective because what we're doing here is an API. So this is not the interesting part really for this talk at least. But it should work. And you see there is the task number one there and that's actually something that's left over from some of our tests. Because at the beginning of our tests, we drop the database, but we don't drop it afterwards. So we have sort of extra data there and might actually be able to see that it was created somewhere here in the software. No, can't find it and it's not about Mongo. So that was pulled out of Mongo and we just sort of, well, finish, talk without. Yes. And there we go. And we can reload and it's still there. So it was back in the database there and we can do things to it. We're not in America, so I can write that. And we're just changing that back and forth. So we have some time left. I could do, well, I actually cheated there. Does anybody notice that? You don't have to do that. Exactly. Thank you. Yeah. So we have like 13 minutes left. So we can spend that on questions or you can go early to lunch or I can do the put implementation. So I'll talk to you guys. It's okay if you go for lunch. Yeah. So in the delete method, the delete method took an integer. But the URL was of course the string. So somewhere in there there is a conversion happening. Yes. True. Yeah. Yeah. And that's, yeah. So we could be explicit about that as well. Go and pass if you wanted to here. So that's what the parameters about the dynamic property you will do. So what happens if you send in something that's not an integer? It will blow up. In a good way or a bad way? Let's try. Well, it's probably just because actually we have an implicit cast down here. Come on. Tell me what the type of that is. Yeah. So that's the type of this is dynamic. So it's not too. So let's just go over to. Try to delete something. It's easiest to do from the test. It's this test. And here we go. And then we'll just delete. So that's not a number as far as I know. There we go. Yeah. So that, yeah. Good question in that sense actually. I said before that Nancy gives you some good error responses, at least in some cases. And I think it does. One thing to notice is that you, when you run from tests at least, you often, you have to scroll a little bit before you get the actual one. Because there's always the configurable bootstrap apart. So that is, so when I knew it up, the browser object that we're using, I had the with arrow, with, that creates a configurable bootstrap. And that's something that can be programmatically configured. So that's instead of my own real bootstrap. And that's all, that will always be on the top of the, of the error messages here. And then once you get past the own nose here is usually where you probably find some, something interesting. Input string was not in the correct format. So let's look at the stack trace, pass int, call site target dot dot dot. So let's see. So it seems to be line 23. That's trying to do the conversion. Yeah. So, sorry. So, so I think it's because try remove takes an integer and then C sharp. So it's the C sharp compiler really that's doing there. That's doing the conversion for you. I'm sure if you put the end pass up here, then you, the explosion would be here. So I guess this sort of demonstrates why I think it is nice to have the dynamic up here where you're pretty close to the HTTP request, which really is just a string. And it is sort of dynamic on the web, right? So it makes sense here. But once I move into sort of my back end business code stuff, I, I tend to like having types actually. Rather have a blow up early here than somewhere deep down, right? It's easier to identify the fault here. Yeah. Other questions? Yeah. Sorry. Well, the, well, if you're in lock, you do it with a new get. Yeah. Connected. Come on. So first we can just actually see the packages that we have installed. Come on. Get back. So these are the, the package that we are using right now. As you see, there's the Nancy package, the HP net hosting one and the testing one, as I mentioned at the start of the talk, we could install other Nancy packages. And there are a, there are quite a few Nancy dot and then tab. Yeah, there we go. And you have all sorts of different things that people have put in and for instance here you have, if you want to use auto fact, you basically just install that or an inject a structure map unity. Why? Windsor, whatever. But I mean, if you want to use other things, like if you want to use a simple injector, for instance, because that's fast. You can also set that up in your bootstrap because what these basically do is that they install a bootstrap for you that goes into Nancy and just says, don't use the container that you used to use this container. And it's, I'm sorry, I don't remember exactly how you, how you do that, but I think it's basically just one method that you need to implement in your, in your bootstrap. Maybe a couple. It's not too much code. And, and the least you can get in there, you, you, I mean, you could just create a project, install one of these and see how they do it. Have something to copy from. I think that should be fine. And it's actually, you're able to, to set it up enough, so to speak, that it's not, that you don't, you're not using two ISE containers at that point. Nancy internally also uses dependency injection for some of the things at least. And if you say, here's simple injector, it will use simple injector. It won't use tiny ISE and simple injector. So you don't get weirdness around that. Yeah, it's good enough. Yeah. What's your experience with the self hosting? Similar. Yeah, so the question is, what is my experience with the self hosting? How does it perform and how stable is it? I don't know how it performs. I must say. And I have, I only have fine experiences with it, stability wise. I haven't run it for long, long periods of time and under heavy load, the self hosting. So, so I don't really have a good answer for your question. I would say, so, so again, that's the split. That's Nancy and that's the, that's the hosting. I'm pretty confident that, that Nancy is fast, at least fast in the sense that I'm not. I'm not fast, at least fast in the sense that I think usually your application code is more likely to be the bottleneck than Nancy itself. And then specifically talking about the self host, then you install the self host there as well. And that's, I mean, that's basically just an ACP listener. So it shouldn't be too complicated either. So, I mean, probably fine. But yeah. So, I'm not sure if you can see this in mono and running in a Linux. Again, that is, that is possible. Everything in Nancy compiles on mono as well. I haven't tried running it there. So I don't really know what you, what you run into. I know people do it and some have deployed it to Heroku, for instance, as well as the cloud service. So, it's doable. I, yeah. So I don't really have a good answer for it. Yeah. I think. Authentication. Authentication. Yes. World domination. World domination, yeah. Oh, yeah. So you have, so again, you can install a NuGet package and get at least some forms of in there of authentication sort of out of the NuGet box. Or again, you can hook into Nancy and implement your own handlers for that. And again, that will be something that you have sort of on the side, an infrastructure part of your code. And then in your modules, you can indicate to Nancy that you want it to be a secure module. So, which I think makes sense because you'll probably often divide the modules up in such a way that there's an area that is, that is a secure part. Maybe it's everything. Maybe it's just one area. But I mean, why not factor it out so it's the whole module? That is secure. And then you, damn it, should be somewhere. There you go. And then that's it. Then depending on the authentication that you have set up, this will be, if you install the forms authentication, then there will be forms authentication. Or you can set up your own sort of key based or token based authentication if you were at Dominic's talk yesterday and heard that you shouldn't use forms authentication. So that's pretty simple and pretty basic, of course, as well. This is only authentication that isn't authorization. Yeah. Well, time for one more question. Documentation. Documentation. No, well, yeah, yeah, that's always a good question. So, well, you can just for yourself. There we go. Let's take a look at the Viki. And so this is the documentation here. And this is the part that you should be looking at over here. So, and there is actual text behind all of these. So, I'm pretty sure I use it and get back. So if we want to look at authentication. So here is the authentication overview. The forms, basic, stateless are the ones that you can just get. And what you do is, oh, there you go. So here's how, so here's stuff about how you do it yourself, securing your resources. You can also do that before and after hook or you can do as I showed it before. Ta-ta-ta requires authentication and ta-ta-ta-ta. I mean, it wasn't prepared that I went into the, exactly the authentication part. This is typical for the documentation. So I actually think it's a good level because you get the information, but it's not information overload and you don't have to read like hundreds of pages or everything. Again, and then if that's not quite enough, I also find it useful to actually go in here to the Nancy code and then you see there are also all these demo applications included in the source code. So you can just, again, look at authentication and then you have a developer application that uses basic here. Oh, so that's a secure module. Let's look at that. Oh, yeah, and it does this and probably has, yeah, it has a bootstrapper. Let's look at that as well. So this seems to be what you need to set up authentication. So that's a, and you're guaranteed that this is, this is up to date. The demos, they work, they run. Yeah. So time is up. I'll be around the rest of the day. I'll be around here. So if you have any more questions, please come talk to me. Thank you. Thank you.
 Hello, so let's get started.  My name is Christian.  My contact stuff is there.  This is the only slide that's going to be there, so we're  switching over to Visual Studio very soon.  So it'll be code, code, code.  But I'm going to just sort of briefly introduce what I'm  going to talk about.  So I'm going to talk about the Nancy web framework, which is  a.NET-based web framework inspired by the Sinatra web  framework, which is a Ruby web framework.  Sinatra is probably the thing that you might use in  Rubyland if you find that Rails is too heavy.  So that's also the thing with Nancy.  It's very lightweight.  It's very, just tries to make it easy for you to work with.  And there are not too many concepts there.  So we'll actually cover quite a few of them just  within this hour.  It's meant to be easy to work with and not get in your way.  And you sort of have the freedom to do what you like to  do, which is all things that I really like.  That also means that Nancy is quite code focused.  So that's why I'm showing it off in Visual Studio instead of  OK, whatever.  Instead of doing it in a lot of slides, because, well, I  think in this case at least, code speaks louder than  slides.  So I'll wait with the slides and over to Visual Studio.  So I've cheated a little bit, not too much, I hope, but a  little bit.  So I've set up a project and a test project as well.  So in Nancy today, we are going to build just sort of a  little back end for a to-do application.  And I've just stolen the Arch typical to-do MVC  application done in Backbone.js.  So it's a single page application where you can  sort of hammer in to-dos.  And you can check them off.  And that's all of it.  And that meant to demonstrate the JavaScript side.  We're not going to look at the JavaScript at all.  We're just going to look at how could you set up a back end  for this, because it's something that throws back  some JSON, and it expects to get some other JSON back  and forth them.  So that's what we're going to build.  So what I've set up here is, well, this up here was just a  Nancy, no, sorry, an empty HPNet application, just  straight out of the box.  And then I've added a couple of Nougat packages to it.  I've added Nancy itself.  And then I've added Nancy.hosting.aspnet.  So Nancy tries to or succeeds in actually separating your  application from what you want to run it on top of.  So there's a Nancy framework itself that's not dependent  on ASPNet or anything like that.  So to actually run your Nancy application, you also need to  have a hosting.  One of the hosting features, one of the hosting options, is  a spinette.  And that's just pretty easy to work with.  You can just F5 in Visual Studio, and you can put it on  IAS, or you could put it in Azure, or on AppHab, or  wherever you want.  But you could also go with doing self-hosting, just as  Web API also does now.  So you could just run your little Nancy application from  within a Windows service or a WPF application or whatever.  So if you have a WPF application deployed across an  enterprise, and you want a backdoor into that, if you want  to be like the NSA, but just within your own organization,  so you want to be able to look inside what everybody's doing  with your application, you just have Nancy and their  self-hosted, and then you have HTTP endpoints.  And you can just go there and look at stuff, well, except  for firewalls, maybe.  But if you control those two, right.  So those are the Nougat packages I have in this, just  Nancy itself and the HPNet hosting.  Then in this project down here, I also have a couple of Nougats  installed, and there's also some test code in there already  that I'll sort of use to drive out what I'm going to code up.  And the Nougats I have in this project are, well, just  X-Unit.  That could be anything.  Just because I like X-Unit.  And then the Nancy dot testing in Nougat.  So probably already sort of becoming clear that Nancy uses  Nougat a lot to distribute the code.  So you're very much, you generally don't really go to  the Nancy site to bring down Nancy.  You just use Nougat packages, and then they have sort of  different parts of Nancy you can install as Nougat packages.  Which is also sort of one of the lightweight parts of Nancy.  It's very modular.  You can sort of change everything and pull stuff out and  put something else in.  So also if you want to have a ViewEngine in there with  Razor, for instance, and Nancy comes out of the box with  another ViewEngine called Super Simple ViewEngine.  But if you want Razor, then you can just  install a Nougat.  Nancy dot ViewEngine is just Razor.  And then you have that.  Nancy out of the box, as we'll see, comes with an IOC  container for dependency injection.  But you can also change that around.  And oh, I don't want to use tiny IOC as Nancy does out of  the box.  I want to use Autofac or whatever, or Castle.  Then you just install a Nougat, and the Nougat changes  things around, and that's it.  So that's very much the experience with Nancy for a  lot of things.  So I've also set up a couple of other things that we're  going to need.  First of all, this is our domain model, our very large and  very intelligent domain model, which is a to-do.  And it's just straight up the same properties as the  single-page front end is going to have in its JSON  representation of the to-do.  So this is just really driven by how that backbone  application works.  I also want to be able to store these to-dos.  It's a back end.  I want to be able to store it.  So I defined this little interface for a data store.  And so you can get back all the to-dos, and you can try to  add them, or you can try to remove them, or you can try  to update one of them.  Pretty simple stuff, and not really too interesting.  And then I've implemented that against MongoDB and  I have a Mongo instance running back here.  You don't have to be able to read all that stuff.  It's just there's a MongoDB running here.  And so the talk is not about Mongo, so I'm not going to  show off the code in there.  Right.  So let's get started, actually.  So I'll start off with this test for what I'm going to  call the home module.  I'll just bring that in there, and move the mouse.  Yes, now you can see the code.  That's good.  And usually there's something red here, because that  refers to a class that doesn't exist.  But let me just explain what's going on here, because I  want to show Nancy from the testing perspective, partly  because the testing support is so cool.  So I think we should focus on that, too.  So what Nancy does testing, that NuGet package I mentioned  before, it brings us, among other things, is this  browser object, or this browser type.  So I can use a browser, and that's going to be my  system under test.  And that is supposed to be sort of my virtual browser.  I want to say that don't think so living in them, and web  driver, and GUI testing, and stuff like that.  This is just an object that knows a bit about Nancy, so  it can give what for Nancy looks like in HTTP request.  It can give that to Nancy, and then they'll flow through the  Nancy stack, but only the Nancy part.  Not the actual HTTP and network stack and all that.  And actually not the HPNet hosting part either.  It just goes straight to Nancy, gives that what looks  like an HTTP request for Nancy.  Nancy doesn't know that it didn't come from a real  browser, but just comes from here.  So it's all in process, and it's fast enough.  And you don't have all the trying to find the right  element, and click in that or moving mouse, or anything  like that.  So this is, in my opinion, much more suited for most tests.  I will just do the request through Nancy, reach your  application code, or our application code, and that  code won't be able to tell the difference either.  So you get sort of a very nice outside view on your  application by testing through this, but without all the  obstacles of testing through the UI, which is horrible.  Also what the browser allows you to do.  It has an API that is, well, it's HTTP.  And that's where exposing stuff for HTTP.  So we want to be able to see that.  So I can say, my system under test, I want to do a get  towards slash.  And the get is an HTTP get.  And I'm doing it to the root of my application.  So these paths are obviously relative to the host.  I get the actual or the response back.  And there are different stuff on the response.  You can get to the body and expect what's in the body of  the response.  Or as I do here, you can get to all the headers.  And just for ease of access, you can easily get to the status  code.  And for this one, I'm just checking that it didn't blow up  and it returned a 200 OK.  So it's not too much.  So first of all, we need this home module.  Oh yeah, might actually also explain this funky bit of  syntax here.  So when Nancy starts up, it wants to make it easy for you  to register your application code with the framework.  So it tries to discover where is your application code and  then just auto-register that.  So you don't have to do a whole lot of configuration.  So we can rely on that from test as well, actually.  Or we can be a bit more specific, which I do like to  be in the tests.  And I can just say, OK, for this test, I want a browser  browser working against Nancy.  But Nancy, with only this module that I'm going to  explicitly tell you what it is.  Just a bit, I'll talk about what a module is.  So it doesn't see all of our application.  It just sees the home module because that's what I actually  want to test against.  And you can put more in here or whatever.  But I think this makes it quite explicit what the scope of  this test actually is.  So I like that.  OK, so we don't have a home module.  So we will add a home module.  Can I remember?  Yes, and it's just a good old C-sharp class.  Home module, yep.  Seems like I can spell.  Get rid of those.  So we have a public class here.  And now to make this a Nancy module, I make it inherit.  Well, Nancy module.  No big surprise.  And so Nancy modules are really at the core when you  work with Nancy.  This is where you actually code up your actual application.  So what you do in a Nancy module is that you set up the  routes that you want your application to be able to  respond to.  And you set up the handlers that you want to use for  responding to that.  So in our case, we need to be able to do a get against slash.  So we tell it that here in the constructor.  So this is telling Nancy that, OK, set up a route at slash  and make that accept the HTTP method get.  And we also want to be able to do something there.  And what, damn keyboard.  There we go.  And we'll just give it a lambda expression telling what we  actually want to do.  And the stuff we want to do is we want to return.  So if I was strictly just trying to make my test pass, I  could just return HTTP status code dot OK here, because  that's what I'll show you then.  But we're going to be a little bit more loose here.  And I'm going to say that I want to return a view.  And the view I want to return is just index.htm, as far as  I remember.  So that refers by convention to a file called index.htm,  obviously.  But that can be placed in a few places.  And one of them, Nancy by convention, will look in your  views folder.  You can alter these conventions if you will.  You can just set up your own, or you can run with Nancy's.  And Nancy is pretty nice about error messages as well.  So if it doesn't find a view that you're trying to  reference, it'll actually tell you all the places it looked,  which is pretty nice.  It's like, oh, that's why.  So you can decide if you want to move it, or you want to put  it into, or if you want to set up your own convention, of  course.  Yeah, it looks like in views index.htm.  And that's just a whole bunch of some HTML.  And that basically just loads up that backbone application  that we saw before.  So along with the JavaScript, I and CSS and stuff I have in  here in content, that's the backbone part.  So we're not going to worry about that.  I just stole that off GitHub.  Right.  So let's see if it compiles, first of all.  It did.  And let's go back to our test.  And let's see if that runs.  And it did.  Yay!  Hello, world in Nancy.  And we got to see Nancy modules.  Right.  That's a success.  So that's not quite enough.  But now we are able to return that HTML page that will  load up the backbone single page application.  That's actually fine.  You can actually work with that.  But it's just all the calls to the back end fails.  You won't see that in the UI, so perceptive.  But if you look in Fittler or something, it'll be quite  obvious.  So we can't actually do anything yet.  So as I said, we want to build a back end for this.  So driving out the next bit a little bit apart here.  So I've just put a little bit of setup here in my  constructor for these tests, because I want to reuse it  across more of the test functions.  You see, again, I'm instantiating a browser here.  Setting up a module.  Now I want to test against the to-dos module.  The to-dos module is the one that actually going to handle  all the requests from our single page application.  Then we also want to set up something more.  I want to set up a dependency, specifically used in this test.  So as I said, at its core and out of the box, NINC gives  you an IOC container, a version of control container, and  it wants you to use dependency injection.  So everybody familiar with dependency injection?  Yeah, good.  And here I just set up a dependency specifically for  this test, and I'm setting up a fake data store.  And that's just a mock of the I data store that we looked  at before, which I'm doing with fake it easy.  Again, that's not really the case.  You could do it with your favorite mocking framework  or by hand or whatever.  Set up the dependency.  That's fine.  And then we have a little bit of test data here.  OK.  So I want to test that when doing a get to slash to-dos.  Well, it's just what I do here.  I want the correct number of to-dos to be returned.  So I set up my mock here to, well, first of all, this is  just creating a list of to-dos.  Then I tell my mock's data store that whenever anybody  calls get all, just return that list of to-dos.  Then I do a get.  And by the way, I say that I would like to have the  response be application JSON.  So this is going to be used in JavaScript, right?  So I want JSON back.  I don't give me HTML or I don't give me anything else.  I want application JSON.  So you can also see it during the request.  This is really close to HTTP or close.  It looks close to HTTP.  We're using the method here.  We're saying what path.  And then here we can set up headers and stuff like that.  Say which type of request.  Do more around the request.  So you can really do a lot of things from the tests here,  which is nice.  Well, since I'm asking to get application JSON, I'm  expecting that I can deserialize the response, the  body of the response as JSON.  And then I can just see, is it the length that I expected?  So that's all well and fine.  Again, this won't compile because we don't have a  to-dos module.  So we will create a to-dos module.  to-dos module.  There we go.  Again, get rid of that.  And obviously, this is, again, a Nancy module just before.  And like before, we are going to set stuff up in the  constructor.  So this time, actually, everything that's going to be  in here is going to be under this last to-dos part of the  application because that's where we want to get the  to-dos back, and that's where we want to post new ones and  stuff like that.  So, OK.  There we go.  Go away, please.  So we just tell the base class, the Nancy module, that  everything in here, go away already, should be relative  to to-dos.  I guess that should be fine like that.  So now we just sort of namespace what we're doing in  here.  And we don't do the get again.  And it's just going to be to slash.  I guess then I maybe not, I probably don't need the  trailing slash up there to-dos.  And we want to do something here again.  And what we want to do here is we want to retrieve all the  to-dos that are stored in our data store right now.  So I'm just sort of going to expect or going to assume for  a minute that I have a data store and that I can say get  all to that.  And then I will actually just return that straight as it is.  And I need a data store.  So I'll take a dependency on the data store.  Data store.  There we go.  And what's it telling me?  Return type is void return type of what?  Is void.  Just compare maybe it tells me something more.  My constructor is returning.  Yeah.  Yeah.  Thank you.  Yes, that's better.  Yeah, yeah, exactly.  My constructor was returning that's true.  And I meant the lambda to return, of course.  Thank you.  So yeah, so this is just returning the list of to-dos  from my data store.  And it's just returning that back into Nancy.  So as you see, the lambdas that we give here to get, for  example, that are going to handle our request, they can do  quite different things.  And Nancy will just pick it up and do what you want.  So here I'm just returning an innumerable of my domain  objects.  And that's fine.  It's going to be fine.  And over here in the home module, I am using Nancy to  locate a view and we're returning that.  That's quite different things.  I could also just return a string, hello world.  That would be the body of the string.  And Nancy would assume that it's text HTML as content type.  Or I could return just a status code.  OK, or 404, or whatever I wanted.  And again, so Nancy will just take these different types  and make them all into a Nancy response.  And be able to output that.  And when you don't specify a whole lot like we don't do  here, Nancy will just sort of assume a few things.  For example, it will assume that it's OK.  So the status code will be set to OK.  And actually Nancy will also, in this case, it will assume  that, OK, you're returning some objects.  You probably want to put them into the body of your  request, or sorry, your response.  And you want to say 200 OK as the status code.  And when I serialize these objects into the body of the  response, you probably want to respect those other tests.  You probably want to respect that accept header that the  client set.  So this should be serialized into Intugation, because that's  what the client asked for.  OK, so crossing my fingers and seeing if this now passes.  It did.  Oh, yeah.  It looks like three tests, just because I'm running it three  times with different amounts.  So zero to do, one to do, and 42 to do, because you always  need 42 in there.  And then just to demonstrate, I have this test down here.  It's almost the exact same thing.  The difference is here.  Now I'm asking for XML, because I still love the 90s.  And I expect to be able to deserialize that as XML.  And let's try to run that.  And that passes as well.  So thank you, Nancy.  Nancy did the content negotiation for me there,  looked at the accept header, and then just serialized  based on the accept header.  So that's nice.  Out of the box.  OK, so now our single-player test is done.  Now our single-page to-do application is able to get  the to-dos that are already on the server side and it'll  display those.  We can't do anything with them yet.  So let's move on to, we'll find for time.  True.  So we also want to be able to create new to-dos on the  client side.  So a bit of setup of the mark I have.  And then that will be a post.  So again, this is the browser object that we look at.  Until now, we've used it with Git.  But we can also use the post method of HTTP, do it to the  same place, and we say this time we just serialize the  body that we want to post.  We serialize that as JSON.  So again, this is setting up a fake HTTP request and HTTP  post, and it's putting a body onto that post.  And the answer from that, we want that to be created and  we want the body in the answer or in the response to  contain the to-do that we just posted.  OK.  Right, so this should fail at the moment.  We haven't done that code, right?  So that fails as expected.  That's nice.  It's set.  See down here, the status code was not found.  No big surprise.  We haven't set up anything to handle post to that endpoint.  Back over here.  And so it's just more of the same, really.  I do that.  And you can do this with all the methods on HTTP.  So if you have the time, at least, we'll show this for  put and delete, and it's really more of the same stuff.  But you can also do patch and hit and options and stuff.  You can set that up.  Or you can leave stuff like options and hit out, and  then Nancy will just handle that for you.  So in the post here, well, we want to do slightly more than  just one liner.  So first of all, let me just open up a scope.  And first of all, we want to get a hold of a new to-do.  So that's the to-do that is being posted into us.  So we are going to ask Nancy to try to create a to-do for us  based on the request.  So model binding in Nancy is done like this.  In our case, we have the to-do in the body.  So Nancy will look in the body here and deserialize that  into a to-do if it can.  It could also look other places like form values if I  remember correctly.  I'm pretty sure it will.  So once we have that, we just work with our data store  again.  If, let's say, if not data store.try.  And I've just made it so it returns false if it wasn't  able to put it in there.  And it's our new to-do.  We want to put in there.  Eh, come on.  And so if it's not able to do that, we will return something.  And in this case, we'll just return an htp status code.  And it will be bad request.  And otherwise, we'll assume that it is added.  So how much did I assert on over here?  I asserted that it was created and that it's, oh, yeah.  I think I said something wrong here.  I think I said that I was asserting that to-do was in  the response.  I'm actually not.  I'm just asserting that we tried to put it into the fake  data store.  So inside of this method, we're just  messing about with the fake.  I've hidden that because it's sort of ugly.  So we will actually just return an htp status code here as  well of created.  Oh, you see, you have all the status codes just named out  here, so you can get to them easily.  And you just need that.  Let's see if it compiles.  And it seems to compile.  And go back, run this.  Right, there we go.  So actually, once again, the bind here sort of respects what's  on the htp-p request.  So in our test here, we explicitly set that using this  helpful method that we wanted the body to be JSON.  So not only does that serialize my to-do object there, the  a to-do, as JSON, but it also sets the htp-hattice to  indicate that the body is JSON.  So the code we have over in the module would also work if  we serialized into XML and then posted that up and set up  the content header server correctly, the content type  correctly.  So again, Nancy respects this content negotiation part of  things, and we'll just pick up.  So out of the box, Nancy can handle JSON and XML in this  way.  If you want to do something more, it's really just a matter  of implementing two different interfaces, one for the  ingoing part for data binding and one for the outgoing part,  the serialization to responses.  Each of those interfaces have two method needs.  And then you can just put that somewhere in your assembly  and Nancy will scan the assembly, find the implementation  of the interfaces, and they'll just hook into this again.  So if you went over to Service Stack maybe and grabbed hold  of their implementation of protocol buffers, because  that's a very efficient protocol, and you plug that  right into those two Nancy interfaces, which is quite  easy.  It's about 50 lines of code combined those two, and that's  even including namespaces and empty lines and stuff like  that.  Then Nancy will pick that up, and you can do the same here.  You can put a protocol buffers serialized to dos into the  body of a post and set the content type.  Nancy will pick that up.  These are a slide for you.  It's fine.  So it's just another example.  It's really easy to just hook into the Nancy engine and just  make stuff still happen.  And stuff like that sets up a nice separation of concerns  as well, because this is sort of my business logic here.  I want to pull out the to do wherever it was in the request,  and then I want to put it in my database.  I don't want to mug about with the exact format here.  It's really nice that that's just an infrastructure thing  that Nancy handles for me.  Nice.  OK.  Looking at time, that's fine.  OK.  Yeah.  So I'm just checking that I don't want to allow saving the  same to do twice.  We'll skip that.  OK.  So as I said, this is just really all the same.  We want to delete something, and we want to do that based on  the ID of the to do.  So every to do has an ID.  And again, because this is how the backbone example expects  things to be, it wants to do an HTTP delete to do slash the  ID of the to do that it wants to delete.  So I'll do that, and then I'll ask my fake if we try to  remove to do number one from the data store.  I'm saying that must have happened.  This is just an assertion with the to do, and then I'll  do the to do.  This is just an assertion with fake it easy.  So to no big surprise at this point, I think this is just  more of the same.  There we go.  And so we just say delete.  And we say, so well, it's slightly different.  We say slash, and then we want to grab hold of the ID.  So we do this like that.  And then actually now we want to, now I'm going to give sort  of the argument for our lambdas a proper name because now  I'm going to use it for something.  That's why I just call it underscore up there.  That underscore is a valid name of an identifier.  I like to use it when I'm not actually using that  variable for anything because it just sort of visually  disappears.  So that indicates to me that I don't care about this right  now.  Whereas when I actually use it, I want to give it a name  that I can read.  So we need the ID out of there.  So I'll just go into the parameters here and I'll just say  ID.  So parameters here is a Nancy type or has a Nancy type,  the type dynamic dictionary.  So that's a dynamic object to no surprise.  And one of the things you can do with that is that you  can just use dot notation to grab hold of whatever you're  capturing up here.  So what you put in here as the path that I want to handle  with this handler, it can be a regular expression.  So and you can have different part captured now.  And this is really just to capture anything behind the  slash to do.  But I could write out a regular expression and saying that  I only want numbers there and I could go on and I could say  I can have another slash and then another part of the  path.  So I can grab different parts.  Anything you can do with a regular expression,  knock yourself out.  And the parts that you name, you can grab just out of here  like that.  So, well, yeah, depending on whether you like a dynamic  or not, this is really cool.  At least it's easy.  I do like it for stuff like that, but I also sort of do  like to move to keep the dynamic parts sort of right here  in the handler, then take the things out and then sort of in  business logic behind.  I want to just be in the typed part of C sharp.  That's a personal opinion.  You can have another one, of course.  So we will try again to delete, try, remove, and that takes  an ID.  So it should be fine.  And, well, let's do it more or less as before.  So if we aren't able to remove it, we will say that  that's because it wasn't found.  Not found.  So that's a 404.  And otherwise, we will return and, well, okay, I guess  would be fine here.  And go compile again.  Let's see if that runs.  And that, yeah.  So we can inspect the to-dos.  We can post new ones and we can delete them.  I am going to skip number five here, which is doing the same  thing, but for put.  And it's just really a handler.  Very similar to what we always see here.  We'll just use data binding like up here to pull out the  to-do of the request.  And then I will try to store that and return static codes  depending on what happened.  We will just skip that because I think you get the picture.  So the last thing actually we want to sort of do here is, well,  we want to start using that Mongo database that I have back  here.  We haven't used that at all up till now because we are just  running against a fake data store.  So actually everything we have done up till now would work if  I didn't have this class in the solution at all, my actual  implementation of the data store.  Because as long as I tell the tests, here is a fake  implementation of the data store.  Well, that's fine for Nancy.  You don't need to have an actual implementation of the  data store.  So you can test drive for a while there and use that also to  drive out the interface for your dependencies and then go  implement them for real once you are all ready for that so you  don't have to guess the interface.  Anyway, we want to hook this up.  So go to this test here.  So this time around we are just first of all making sure that  there's no sort of old data in our Mongo database so we can  connect to the database that I have a local host at the default  port and I just drop it.  So I have some empty data to work on.  And then I set up a browser again.  This time I don't sort of inline do the configuration of it.  This time I want this to be an integration test.  So I want to run off the bootstrap that I'm going to create  in a little bit and that is going to be the bootstrap that  the application is actually going to run on.  And then we just have a test that posts a to do as JSON.  And then once it's finished doing that it does a get to the  same place again and says oh I want it as JSON.  It tries to deserialize the body of the response from all of this.  So this is two calls changed together.  So this is nice if you sort of want to test a scenario.  Of course this can also get out of hand if you have really long  scenarios so you have to use judgment there.  You actually have to think even though it is easy.  But this is fine just doing two first posting and then getting.  And the response here is the response from the last one.  That would get out to no big surprise.  And then I just assert that I get back what I expected there.  That I get back the one that I just posted.  So and since I'm going to run on sort of the real bootstrap  where there's an integration test that sees that I can store it  back to the Mongo database and retrieve it from the Mongo database  as well. So just sort of a smoke test that everything is connected correctly.  So I don't have a bootstrap yet.  And this is the part of the code where I'm going to cheat a little bit.  So I'm just going to show all files for a little bit.  And then I'm going to grab a bootstrap that I already have here.  And we're going to look at that.  So until now we've just used Nancy in the default configuration.  We haven't even taken advantage of the fact that we have installed the  ASP.NET hosting because we haven't run it on a web server yet.  We've just run it from the tests.  So but if you want to do, when you need to do to configure something  for yourself, you do that in the bootstrap.  You just have to have a class that implements the I-Nancy bootstrap,  I believe, as the interface.  And typically the easiest way to do that is actually inheriting from the  default Nancy bootstrap.  So up until now we would have just run actually off of the default Nancy bootstrap,  which well, set up all the defaults.  But I want to do a little bit more now.  I want to do a little bit configuration of the IFC container.  First I just call base.  And one of the things that it will do up here is it will auto register  everything that it can sort of find.  So actually if our Mongo data store, so the implementation of the I data store,  if that didn't take any arguments, if that was just something that was  coming up, I wouldn't be able, I wouldn't have to do anything here.  Nancy would be able to just pick up that I have an implementation of the  I data store, which one of my modules needs, and it doesn't take any  arguments for its constructor, so well, it's fine.  We'll just new that up and assume that that is the, that is the dependency.  But since I need to pass in a connection string, I sort of want to take more control.  I probably don't want to hard code that either, but demo code.  So I just knew up the one connection here to the data store and registered that  with the container.  And just to demonstrate, there are sort of various parts that you can get into  here and set up all kinds of different things within Nancy.  But oftentimes you don't actually have to do a whole lot here because Nancy  is really good at auto discovering things.  Like I said before, if you want to implement your own serializer, fine,  just to let Nancy discover it by itself.  But if for whatever reason you want to take control of how that's instantiated  or something like that, again, you can do that in the bootstrap and you can  set up the body deserializers that you want here.  So very modular, very much on the conventional configuration side,  but it does allow you to go in and change everything basically within the framework  itself.  Right.  So now we have a bootstrap per.  We have the Mongo database running.  We go back to our test here.  Let's see if it compiles at least, it seems to do.  And then let's just run this integration just and we can maybe, oh, yeah,  it's doing something with the Mongo database.  You don't have to read that, but you can just see that something is going on at  least and seems to be done.  And it actually seems to have succeeded as well.  So why don't we actually try to, well, let's compile so we should be able to,  this is how the application looked before the session started.  If you went to the last, we just had Nancy's very nice custom 404 page there.  Oh, and you can see up here, it's a Nancy application.  So that's, yeah, so anybody recognize her?  It's Nancy Sinatra seen from the site.  So let's see what happens if I go there again.  So this is, well, you can't really see that this is, well, you can see that there actually.  This is just exactly how this to do MVC would look running against anything, of course.  You're just seeing the JavaScript here, which is why I'd rather see it from the test perspective  because what we're doing here is an API.  So this is not the interesting part really for this talk at least.  But it should work.  And you see there is the task number one there and that's actually something that's left over from some of our tests.  Because at the beginning of our tests, we drop the database, but we don't drop it afterwards.  So we have sort of extra data there and might actually be able to see that it was created somewhere here in the software.  No, can't find it and it's not about Mongo.  So that was pulled out of Mongo and we just sort of, well, finish, talk without.  Yes.  And there we go.  And we can reload and it's still there.  So it was back in the database there and we can do things to it.  We're not in America, so I can write that.  And we're just changing that back and forth.  So we have some time left.  I could do, well, I actually cheated there.  Does anybody notice that?  You don't have to do that.  Exactly.  Thank you.  Yeah.  So we have like 13 minutes left.  So we can spend that on questions or you can go early to lunch or I can do the put implementation.  So I'll talk to you guys.  It's okay if you go for lunch.  Yeah.  So in the delete method, the delete method took an integer.  But the URL was of course the string.  So somewhere in there there is a conversion happening.  Yes.  True.  Yeah.  Yeah.  And that's, yeah.  So we could be explicit about that as well.  Go and pass if you wanted to here.  So that's what the parameters about the dynamic property you will do.  So what happens if you send in something that's not an integer?  It will blow up.  In a good way or a bad way?  Let's try.  Well, it's probably just because actually we have an implicit cast down here.  Come on.  Tell me what the type of that is.  Yeah.  So that's the type of this is dynamic.  So it's not too.  So let's just go over to.  Try to delete something.  It's easiest to do from the test.  It's this test.  And here we go.  And then we'll just delete.  So that's not a number as far as I know.  There we go.  Yeah.  So that, yeah.  Good question in that sense actually.  I said before that Nancy gives you some good error responses, at least in some cases.  And I think it does.  One thing to notice is that you, when you run from tests at least, you often, you have to scroll a little bit before you get the actual one.  Because there's always the configurable bootstrap apart.  So that is, so when I knew it up, the browser object that we're using, I had the with arrow, with, that creates a configurable bootstrap.  And that's something that can be programmatically configured.  So that's instead of my own real bootstrap.  And that's all, that will always be on the top of the, of the error messages here.  And then once you get past the own nose here is usually where you probably find some, something interesting.  Input string was not in the correct format.  So let's look at the stack trace, pass int, call site target dot dot dot.  So let's see. So it seems to be line 23.  That's trying to do the conversion.  Yeah.  So, sorry.  So, so I think it's because try remove takes an integer and then C sharp.  So it's the C sharp compiler really that's doing there.  That's doing the conversion for you.  I'm sure if you put the end pass up here, then you, the explosion would be here.  So I guess this sort of demonstrates why I think it is nice to have the dynamic up here where you're pretty close to the HTTP request, which really is just a string.  And it is sort of dynamic on the web, right?  So it makes sense here.  But once I move into sort of my back end business code stuff, I, I tend to like having types actually.  Rather have a blow up early here than somewhere deep down, right? It's easier to identify the fault here.  Yeah.  Other questions?  Yeah.  Sorry.  Well, the, well, if you're in lock, you do it with a new get.  Yeah.  Connected.  Come on.  So first we can just actually see the packages that we have installed.  Come on.  Get back.  So these are the, the package that we are using right now.  As you see, there's the Nancy package, the HP net hosting one and the testing one, as I mentioned at the start of the talk, we could install other Nancy packages.  And there are a, there are quite a few Nancy dot and then tab.  Yeah, there we go.  And you have all sorts of different things that people have put in and for instance here you have, if you want to use auto fact, you basically just install that or an inject a structure map unity.  Why?  Windsor, whatever.  But I mean, if you want to use other things, like if you want to use a simple injector, for instance, because that's fast.  You can also set that up in your bootstrap because what these basically do is that they install a bootstrap for you that goes into Nancy and just says, don't use the container that you used to use this container.  And it's, I'm sorry, I don't remember exactly how you, how you do that, but I think it's basically just one method that you need to implement in your, in your bootstrap.  Maybe a couple. It's not too much code.  And, and the least you can get in there, you, you, I mean, you could just create a project, install one of these and see how they do it.  Have something to copy from.  I think that should be fine.  And it's actually, you're able to, to set it up enough, so to speak, that it's not, that you don't, you're not using two ISE containers at that point.  Nancy internally also uses dependency injection for some of the things at least.  And if you say, here's simple injector, it will use simple injector.  It won't use tiny ISE and simple injector.  So you don't get weirdness around that.  Yeah, it's good enough.  Yeah.  What's your experience with the self hosting?  Similar.  Yeah, so the question is, what is my experience with the self hosting?  How does it perform and how stable is it?  I don't know how it performs.  I must say.  And I have, I only have fine experiences with it, stability wise.  I haven't run it for long, long periods of time and under heavy load, the self hosting.  So, so I don't really have a good answer for your question.  I would say, so, so again, that's the split.  That's Nancy and that's the, that's the hosting.  I'm pretty confident that, that Nancy is fast, at least fast in the sense that I'm not.  I'm not fast, at least fast in the sense that I think usually your application code is more likely to be the bottleneck than Nancy itself.  And then specifically talking about the self host, then you install the self host there as well.  And that's, I mean, that's basically just an ACP listener.  So it shouldn't be too complicated either.  So, I mean, probably fine.  But yeah.  So, I'm not sure if you can see this in mono and running in a Linux.  Again, that is, that is possible.  Everything in Nancy compiles on mono as well.  I haven't tried running it there.  So I don't really know what you, what you run into.  I know people do it and some have deployed it to Heroku, for instance, as well as the cloud service.  So, it's doable.  I, yeah.  So I don't really have a good answer for it.  Yeah.  I think.  Authentication.  Authentication.  Yes.  World domination.  World domination, yeah.  Oh, yeah.  So you have, so again, you can install a NuGet package and get at least some forms of in there of authentication sort of out of the NuGet box.  Or again, you can hook into Nancy and implement your own handlers for that.  And again, that will be something that you have sort of on the side, an infrastructure part of your code.  And then in your modules, you can indicate to Nancy that you want it to be a secure module.  So, which I think makes sense because you'll probably often divide the modules up in such a way that there's an area that is, that is a secure part.  Maybe it's everything.  Maybe it's just one area.  But I mean, why not factor it out so it's the whole module?  That is secure.  And then you, damn it, should be somewhere.  There you go.  And then that's it. Then depending on the authentication that you have set up, this will be, if you install the forms authentication, then there will be forms authentication.  Or you can set up your own sort of key based or token based authentication if you were at Dominic's talk yesterday and heard that you shouldn't use forms authentication.  So that's pretty simple and pretty basic, of course, as well.  This is only authentication that isn't authorization.  Yeah.  Well, time for one more question.  Documentation.  Documentation.  No, well, yeah, yeah, that's always a good question.  So, well, you can just for yourself.  There we go.  Let's take a look at the Viki.  And so this is the documentation here.  And this is the part that you should be looking at over here.  So, and there is actual text behind all of these.  So, I'm pretty sure I use it and get back.  So if we want to look at authentication.  So here is the authentication overview.  The forms, basic, stateless are the ones that you can just get.  And what you do is, oh, there you go.  So here's how, so here's stuff about how you do it yourself, securing your resources.  You can also do that before and after hook or you can do as I showed it before.  Ta-ta-ta requires authentication and ta-ta-ta-ta.  I mean, it wasn't prepared that I went into the, exactly the authentication part.  This is typical for the documentation.  So I actually think it's a good level because you get the information,  but it's not information overload and you don't have to read like hundreds of pages or everything.  Again, and then if that's not quite enough, I also find it useful to actually go in here  to the Nancy code and then you see there are also all these demo applications included in the source code.  So you can just, again, look at authentication and then you have a developer application that uses basic here.  Oh, so that's a secure module. Let's look at that.  Oh, yeah, and it does this and probably has, yeah, it has a bootstrapper.  Let's look at that as well.  So this seems to be what you need to set up authentication.  So that's a, and you're guaranteed that this is, this is up to date.  The demos, they work, they run.  Yeah. So time is up.  I'll be around the rest of the day. I'll be around here.  So if you have any more questions, please come talk to me. Thank you.  Thank you.
Christian_Horsdal_-_Test_Driving_Nancy-68378951_0_107.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_108_1895.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_1896_3239.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_3240_4139.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_4140_4583.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_4584_5759.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_5760_6719.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_6720_9383.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_9384_9503.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_9504_10055.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_10056_10115.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_10116_10523.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_10524_17963.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_17964_18407.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_18408_18515.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_18516_18659.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_18660_18731.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_18732_18923.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_18924_19019.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_19020_19643.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_19644_19703.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_19704_19763.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_19764_20063.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_20064_20903.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_20904_22859.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_22860_23015.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_23016_26387.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_26388_29435.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_29436_29735.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_29736_29843.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_29844_30407.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_30408_30479.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_30480_30551.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_30552_30899.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_30900_31091.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_31092_31151.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_31152_31199.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_31200_31247.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_31248_31307.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_31308_31787.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_31788_31907.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_31908_32075.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_32076_32135.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_32136_32183.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_32184_32231.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_32232_32303.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_32304_32423.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_32424_32543.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_32544_33095.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_33096_33155.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_33156_33383.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_33384_33443.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_33444_34115.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_34116_34487.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_34488_34583.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_34584_34751.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_34752_34895.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_34896_35339.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_35340_35435.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_35436_35603.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_35604_35699.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_35700_35759.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_35760_40259.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_40260_40403.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_40404_43811.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_43812_48875.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_48876_49955.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_49956_51311.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_51312_56231.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_56232_56831.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_56832_56939.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_56940_57611.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_57612_60239.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_60240_68675.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_68676_69683.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_69684_71999.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_72000_72131.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_72132_77975.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_77976_87395.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_87396_87479.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_87480_87599.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_87600_87731.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_87732_88271.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_88272_88343.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_88344_88415.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_88416_88583.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_88584_88835.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_88836_89579.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_89580_89963.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_89964_90035.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_90036_90971.png Christian_Horsdal_-_Test_Driving_Nancy-68378951_90972_91065.png