Sunday, March 24, 2013

Simple.Web and F#

I have become increasingly more interested in F# and hope, someday, to transition over from C# to F# in my day-to-day work. (More on that later.)

I have also become increasingly more interested in Simple.Web by Mark Rendle and hope, someday, to transition over from ASP.NET MVC to Simple.Web in my day-to-day work. (More on that later, as well.)

Given both my fondness of F# and Simple.Web, I decided to try to marry the two. I created the normal ASP.NET Empty Project and added the Simple.Web.Aspnet NuGet package along with the Simple.Web.Razor NuGet package. Then I created a handlers project (F# Class Library), referenced it from the first project, and added the same NuGet package. I created a simple handler for the site's index in the first project.

namespace SimpleTest.Web

open Simple.Web

[<UriTemplate("/")>]
type Class1() =
    interface IGet with
        member this.Get() = Status.OK

I also added a Razor page to handle my get request and then hit F5 to fire it up. Since it was so simple, I was expecting to see my fake homepage and see my little test succeed. Wrong.

Instead, I am greeted with a yellow screen of death telling me "Object reference not set to an instance of an object." Hmmm...

I created the same class in C# and everything worked fine. I was beginning to wonder if this was going to work out at all. Well, after some fiddling, I got it to work. Why it works, I am not really sure about. (I'm far from completely comfortable in F#, let alone an expert.) All I had to do was add a member function that mirrored my interface version of Get().

namespace SimpleTest.Web

open Simple.Web

[<UriTemplate("/")>]
type Class1() =
    member this.Get() = Status.OK

    interface IGet with
        member this.Get() = Status.OK

Of course that means I can just return the result of the member function from the interface implementation. But, that got me to thinking. I decided, in the interface implementation, to throw a NotImplementedException:

namespace SimpleTest.Web

open System
open Simple.Web

[<UriTemplate("/")>]
type Class1() =
    member this.Get() = Status.OK

    interface IGet with
        member this.Get() = raise(new NotImplementedException())

To my amusement, it still loaded my page. I understand that to access an interface-defined method in F# you have to explicitly cast the instance to the interface you're targeting. I just did not know that applied across the language boundaries as well when doing whatever magic Simple.Web is doing to find the applicable method.

Time to hunt that down next.

Applying Attributes to Tool-Generated Classes

Something I ran into a while back, using Linq to SQL (prior to EF Code First), was getting some meta-data onto the models generated by Visual Studio. If you add an Attribute to the generated code, there is a pretty good chance you are going to lose your hard work whenever you re-run the tool. I found this question, on Stack Overflow, asking about this exact problem and was able to help somebody out. I figured this would be a great place to share the code, as well, so I don't have to search for it later.

Most of the tool-generated code that we run into is going to be made out of a partial class. All we have to do is create a new class with the same name (partial as well, of course) and decorate it with the MetadataTypeAttribute. We supply the attribute with the type of a new class that looks like the class we want to extend with the attributes we want to apply. This probably sounds more complicated than it is. So, some sample code should help.

Let's say that our tool generated a class called "GeneratedByOurTool" and it has a string property "SomeProperty"—original, I know. Now we want to pass that directly to some MVC view (yuck, I know) and want to use the Razor helper methods to generate a label for our property. Well, in its current state, Razor is going to write out "SomeProperty" because it doesn't know that you really wanted a space in there.

namespace ExtendingGeneratedCode
{
    using System.ComponentModel.DataAnnotations;

    // This class is generated by a tool
    public partial class GeneratedBySomeTool
    {
        public string SomeProperty { get; set; }
    }


    // We hand wrote the following two
    [MetadataType (typeof (MyMetadataClass))]
    public partial class GeneratedBySomeTool { }

    public class MyMetadataClass
    {
        [Display (Name = "Some Property")]
        public string SomeProperty { get; set; }
    }
}

So, we create a new partial class (in a new file, otherwise it can be overwritten by the tool!) with the same name as the one generated by the tool. In our case, it is "GeneratedBySomeTool." We then decorate our new class with the MetadataTypeAttribute and point it to a third class that holds the metadata in which we are interested.

I will normally put the two hand-written classes into the same file to save from having to hunt them down. Also, note, the hand-written version of GeneratedBySomeTool is empty. You can put new properties or methods here, though, if you want to extend the tool-generated version of the class without using inheritance.

MetadataTypeAttribute resides in the System.ComponentModel.DataAnnotations assembly, in the same namespace as the assembly.

Thursday, March 21, 2013

Testing an ASP.NET MVC Controller

I am a fan of testing my code and have been using SpecsFor, written by Matt Honeycutt, for a lot of my BDD-style unit testing lately. It makes testing very clear and easy to read, comes baked-in with Moq, StructureMap, and a whole lot more goodness.

One of the pain-points of testing ASP.NET MVC is dealing with the controller context, request, response, server, etc. So, I have extended SpecsFor a little bit for my controllers (in both F# and C#).

C#

namespace SpecsForExtensions
{
    using System;
    using System.Security.Principal;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;

    public class ControllerSpecsFor<T> : DbSpecsFor<T>
        where T : Controller
    {
        protected override void InitializeClassUnderTest ()
        {
            base.InitializeClassUnderTest ();

            var context = GetMockFor<HttpContextBase> ();
            var session = GetMockFor<HttpSessionStateBase> ();
            var request = GetMockFor<HttpRequestBase> ();
            var response = GetMockFor<HttpResponseBase> ();
            var server = GetMockFor<HttpServerUtilityBase> ();
            var user = GetMockFor<IPrincipal> ();
            var identity = GetMockFor<IIdentity> ();
            var uri = new Uri ("http://localhost:12345");
            var requestContext = new RequestContext (context.Object, new RouteData ());

            context.Setup (x => x.Session).Returns (session.Object);
            context.Setup (x => x.Server).Returns (server.Object);
            context.Setup (x => x.Request).Returns (request.Object);
            context.Setup (x => x.Response).Returns (response.Object);
            context.Setup (x => x.User).Returns (user.Object);
            request.Setup (x => x.Url).Returns (uri);
            user.Setup (x => x.Identity).Returns (identity.Object);

            SUT.ControllerContext = new ControllerContext (context.Object, new RouteData (), SUT);
            SUT.Url = new UrlHelper (requestContext);
        }
    }
}

F#

namespace SpecsForExtensions

open System
open System.Security.Principal
open System.Web
open System.Web.Mvc
open System.Web.Routing
open SpecsFor

type ControllerSpecsFor<'a when 'a : not struct and 'a :> Controller>() =
    inherit SpecsFor<'a>()

    override this.InitializeClassUnderTest() =
        base.InitializeClassUnderTest()

        let context = this.GetMockFor<HttpContextBase>()
        let session = this.GetMockFor<HttpSessionStateBase>()
        let request = this.GetMockFor<HttpRequestBase>()
        let response = this.GetMockFor<HttpResponseBase>()
        let server = this.GetMockFor<HttpServerUtilityBase>()
        let user = this.GetMockFor<IPrincipal>()
        let identity = this.GetMockFor<IIdentity>()
        let uri = new Uri("http://localhost:1234")
        let requestContext = new RequestContext(context.Object, new RouteData())

        context.Setup<_>(fun x -> x.Session).Returns(session.Object) |> ignore
        context.Setup<_>(fun x -> x.Server).Returns(server.Object) |> ignore
        context.Setup<_>(fun x -> x.Request).Returns(request.Object) |> ignore
        context.Setup<_>(fun x -> x.Response).Returns(response.Object) |> ignore
        context.Setup<_>(fun x -> x.User).Returns(user.Object) |> ignore
        request.Setup<_>(fun x -> x.Url).Returns(uri) |> ignore
        user.Setup<_>(fun x -> x.Identity).Returns(identity.Object) |> ignore

        this.SUT.ControllerContext <- new ControllerContext (context.Object, new RouteData(), this.SUT)
        this.SUT.Url <- new UrlHelper(requestContext)