Monday, December 31, 2007

Rhino Mocks Callbacks with Lambda expressions

I recently wanted to write a test for this piece of code using Rhino Mocks:

public void Register(string username, string password, string posterName)
{
    User user = new User()
    {
        Name = username,
        Password = password,
        Role = Role.Poster
    };

    userRepository.Save(user);

    Poster poster = new Poster()
    {
        Name = posterName,
        ContactName = username,
        PosterUsers = new System.Collections.Generic.List<PosterUser>() { new PosterUser() { User = user } }
    };

    posterRepository.Save(poster);
}

As you can see, it simply takes some parameters: username, password, posterName and then constructs a User object which it passes to the Save method of a userRepository and a Poster object which is passed to the Save method of a posterRepository. Both userRepository and posterRepository are created using Inversion of Control. Here's my initial attempt at a unit test for the Register method:

[Test]
public void ReigsterShouldSaveNewUserAndPoster()
{
    string username = "freddy";
    string password = "fr0dd1";
    string posterName = "the works";

    using (mocks.Record())
    {
        // how do get the user object that should be passed to userRepository ???
        //userRepository.Save(????);

        //posterRepository.Save(????);
    }

    using (mocks.Playback())
    {
        posterLoginController.Register(username, password, posterName);
        
        // here I need to assert that the correct User and Poster objects were passed to the 
        // repository mocks
    }
}

I couldn't see how to get the object that was passed to my mock instances of userRepository and posterRepository. I needed some way of capturing the arguments to the mock objects so that I could assert they were correct in the playback part of the test.

Alternatively, I briefly considered using abstract factories to create my user and repository:

public void Register(string username, string password, string posterName)
{
    User user = userFactory.Create(username, password, Role.Poster);

    userRepository.Save(user);

    Poster poster = posterFactory.Create(posterName, username, user);

    posterRepository.Save(poster);
}

Then I could have written the tests like this:

[Test]
public void ReigsterShouldSaveNewUserAndPoster()
{
    string username = "freddy";
    string password = "fr0dd1";
    string posterName = "the works";

    using (mocks.Record())
    {
        User user = new User();
        Expect.Call(userFactory.Create(username, password, Role.Poster)).Return(user);
        userRepository.Save(user);

        Poster poster = new Poster();
        Expect.Call(posterFactory.Create(posterName, username, user)).Return(poster);
        posterRepository.Save(poster);
    }

    using (mocks.Playback())
    {
        posterLoginController.Register(username, password, posterName);
    }
}

It makes for a very clean test and it's forced me to separate out the concerns of creating new instances of User and Poster and the co-ordination of creating and saving them which is what the controller method 'Register' is really about. But in this case I just felt it was overkill to create abstract factories just to make my tests neat.

A bit more digging in the Rhino Mocks documentation revealed Callbacks. They allow you to create a delegate to do custom validation of the mocked method call. It effectively allows you to override Rhino Mocks' usual processing and plug in your own. The delegate has to have the same input parameters as the method it's validating and return a boolean; true if your test passes; false if it fails.

It's quite easy to use callbacks with the Linq Func delegate and Lambda expressions, although it took me a few attempts to work it all out. Here's the test:

[Test]
public void ReigsterShouldSaveNewUserAndPoster()
{
    string username = "freddy";
    string password = "fr0dd1";
    string posterName = "the works";

    User user = null;
    Poster poster = null;

    using (mocks.Record())
    {
        userRepository.Save(null);
        LastCall.Callback(new Func<User, bool>( u => { user = u; return true; }));

        posterRepository.Save(null);
        LastCall.Callback(new Func<Poster, bool>(p => { poster = p; return true; }));
    }

    using (mocks.Playback())
    {
        posterLoginController.Register(username, password, posterName);

        Assert.AreEqual(username, user.Name);
        Assert.AreEqual(password, user.Password);
        
        Assert.AreEqual(posterName, poster.Name);
        Assert.AreEqual(user, poster.PosterUsers[0].User);
    }
}

It's quite awkward syntax and it's not at all obvious how you'd do this from the Rhino Mocks API. I don't like the LastCall syntax either, but from this blog post it looks like that something that Oren is working on. There also Moq, which is a new mocking framework that fully leverages the new C# language features. Definitely something to keep an eye on.

Friday, December 07, 2007

RESTful Web Services by Leonard Richardson & Sam Ruby

restfull_web_services

I've had some experience creating web services. I've been involved in a couple of major SOA projects and even had a go at writing my own web service test tool: WsdlWorks, although I've sadly not had enough time to finish it off. So I've been soaked in SOAP (sorry) for quite some time, especially the Microsoft way of doing it. I've been aware of an alternative web service universe, REST, for a while too, but I dismissed it for lacking tooling and widespread adoption. However, in response to a comment on my 'REST as Betamax' blog, I went out and bought RESTful Web Services by Richardson & Ruby. Yes, it's one of those books, you know, the ones that totally kick you out of your rut. After reading it I've had my mind comprehensively changed. OK, so I'm of weak and easily lead by a good argument, but R&R's arguments for REST are pretty compelling. The core goodness is the linkability of RESTful services; the way that every resource (think object) has an individual URL which allows true hyper-linking of machine readable information. It's the restoration of HTTP to it's throne as the core of the World Wide Web. They show that SOAP is not only needless complexity, but actually a step backward.

As for lack of tooling, I was impressed by how nicely Ruby on Rails supports REST and it leads to the obvious conclusion that the new MVC Framework will make an excellent REST platform, especially with its powerful URL Routing engine. However, for the time being 99% of MS shops will not embrace REST no matter how powerful the arguments are for it. If they're keeping up to date they'll be writing RPC based web services with WCF, if not, they'll still be annotating stuff with [WebMethod]. But, if you hired me and gave me a free hand, I'd definitely go with REST.

Reading this book has just reinforced my view that Microsoft spends most of its time playing catchup with the rest of the software world. Thinking about it, none of the books that have had the most influence on the way I write software mention Microsoft tools.

Exposing generic methods to frameworks that don't understand generics.

How do you wrap a generic method like this:

public string WriteHtml<T>(string target, T source)

so that frameworks which don't understand generics can understand it, like this:

public string WriteHtml(string target, object source)

I've been writing a web application with Monorail. It's been loads of fun, and the speed at which I've been able to get a fully featured application up and running is awesome. I'll be blogging more about this soon, but today I just want to make a quick 'howto' about exposing generic APIs to frameworks that don't understand generics.

The problem is this: Monorail's default view engine is NVelocity, a port of the java Velocity template engine. I've found it pretty nice so far. To use it you just add stuff into a 'PropertyBag' in your controller and then reference in your view using the $parameterName syntax. You can invoke methods on your types and indeed Monorail comes with a nice collection of helpers to use within the view. I wanted to create a new helper to display a tree of objects (in this case a location hierarchy) in a combo box like this:

LocationSelect

So in my favorite TDD style I quickly got a class (HierarchicalSelect) up and running that took any object of type t with a property of IList<t> and spat out the HTML for my desired combo box. I then referenced my new helper in my template like this:

$hierarchicalSelect.WriteHtml("location.id", $location)

But it didn't work. Unhappily, NVelocity doesn't throw, it simply outputs your placeholder when it can't bind. I eventually worked out that the reason it didn't like WriteHtml() was because it's an generic method:

public string WriteHtml<T>(string target, T source)

What I needed was a way of exposing WriteHtml with the source argument typed as object rather than T. It turned out to be quite an effort. This is the obvious way:

public string WriteHtml(string target, object source)
{
    return WriteHtml(target, source);
}

But of course it doesn't work because T becomes System.Object rather than the actual type of 'source'.

Instead you have to use reflection to create a MethodInfo for WriteHtml<T> and then create a typed version before invoking it.

public string WriteHtml(string target, object source)
{
    Type sourceType = source.GetType();
    
    // get our unambiguous generic method
    MethodInfo writeHtmlMethod = typeof(HierarchicalSelect).GetMethod("WriteHtmlUnambiguous", 
        BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance);

    // create a typed version
    MethodInfo typedWriteHtmlMethod = writeHtmlMethod.MakeGenericMethod(sourceType);
    
    // invoke it.
    return (string)typedWriteHtmlMethod.Invoke(this, new object[] { target, source });
}

// need to provide this so that GetMethod can unambiguously find it.
private string WriteHtmlUnambiguous<T>(string target, T source)
{
    return WriteHtml<T>(target, source, null);
}

Just for your entertainment. Here's the full code for HierarchicalSelect:

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Reflection;
using System.Web;
using System.Web.UI;
using System.Linq;

namespace Hadlow.PropertyFinder.HtmlHelpers
{
    public class HierarchicalSelect
    {
        // NVelocity doesn't support generic methods :(
        // we have to take an object argument and then construct one of the generic methods first
        // before calling it.
        public string WriteHtml(string target, object source)
        {
            Type sourceType = source.GetType();
            
            // get our unambiguous generic method
            MethodInfo writeHtmlMethod = typeof(HierarchicalSelect).GetMethod("WriteHtmlUnambiguous", 
                BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance);

            // create a typed version
            MethodInfo typedWriteHtmlMethod = writeHtmlMethod.MakeGenericMethod(sourceType);
            
            // invoke it.
            return (string)typedWriteHtmlMethod.Invoke(this, new object[] { target, source });
        }

        // need to provide this so that GetMethod can unambiguously find it.
        private string WriteHtmlUnambiguous<T>(string target, T source)
        {
            return WriteHtml<T>(target, source, null);
        }

        public string WriteHtml<T>(string target, T source)
        {
            return WriteHtml<T>(target, source, null);
        }

        public string WriteHtml<T>(string target, T source, IDictionary attributes)
        {
            string id = CreateHtmlId(target);
            string name = target;

            StringBuilder sb = new StringBuilder();
            StringWriter sbWriter = new StringWriter(sb);
            HtmlTextWriter writer = new HtmlTextWriter(sbWriter);

            writer.WriteBeginTag("select");
            writer.WriteAttribute("id", id);
            writer.WriteAttribute("name", name);
            writer.Write(" ");
            writer.Write(GetAttributes(attributes));
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.WriteLine();

            if (source != null)
            {
                HierarchicalThing<T> root = new HierarchicalThing<T>(source);
                WriteOptions(writer, root, 0);
            }

            writer.WriteEndTag("select");

            return sbWriter.ToString();
        }

        private void WriteOptions<T>(
            HtmlTextWriter writer, 
            HierarchicalThing<T> item, 
            int level)
        {
            writer.WriteBeginTag("option");

            //if (item.IsSelected)
            //{
            //    writer.Write(" selected=\"selected\"");
            //}

            writer.WriteAttribute("value", HttpUtility.HtmlEncode(item.Id));
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.Write(GetLevelString(level) + HttpUtility.HtmlEncode(item.ToString()));
            writer.WriteEndTag("option");
            writer.WriteLine();

            level++;
            foreach (HierarchicalThing<T> child in item.Children)
            {
                WriteOptions(writer, child, level);
            }
        }

        private string GetLevelString(int level)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < level; i++)
            {
                sb.Append("    ");
            }
            return sb.ToString();
        }

        private string CreateHtmlId(string name)
        {
            StringBuilder sb = new StringBuilder(name.Length);

            bool canUseUnderline = false;

            foreach (char c in name.ToCharArray())
            {
                switch (c)
                {
                    case '.':
                    case '[':
                    case ']':
                        if (canUseUnderline)
                        {
                            sb.Append('_');
                            canUseUnderline = false;
                        }
                        break;
                    default:
                        canUseUnderline = true;
                        sb.Append(c);
                        break;
                }

            }

            return sb.ToString();
        }

        private string GetAttributes(IDictionary attributes)
        {
            if (attributes == null || attributes.Count == 0) return string.Empty;

            StringBuilder contents = new StringBuilder();

            foreach (DictionaryEntry entry in attributes)
            {
                if (entry.Value == null || entry.Value.ToString() == string.Empty)
                {
                    contents.Append(entry.Key);
                }
                else
                {
                    contents.AppendFormat("{0}=\"{1}\"", entry.Key, entry.Value);
                }
                contents.Append(' ');
            }

            return contents.ToString();
        }

        /// <summary>
        /// Represents a hierarchial object with one property that has a type of 
        /// IList<sametype>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        private class HierarchicalThing<T>
        {
            readonly T source;
            readonly PropertyInfo childrenProperty;
            readonly string id;

            public string Id
            {
                get { return id; }
            }

            public HierarchicalThing(T source)
            {
                this.source = source;
                Type sourceType = source.GetType();

                childrenProperty = FindChildProperty(sourceType);
                id = FindIdValue(source);

                if (childrenProperty == null)
                {
                    throw new ApplicationException("The source object must have a property that " +
                        "represents it's children. This property much have a type of IList<source type>. " +
                        "No such property was found in type: " + sourceType.Name);
                }
            }

            private string FindIdValue(T source)
            {
                return source.GetType().GetProperties().First(p =>
                    p.GetCustomAttributes(true).Any(a =>
                        a is Castle.ActiveRecord.PrimaryKeyAttribute)
                        ).GetValue(source, null).ToString();
            }

            private PropertyInfo FindChildProperty(Type sourceType)
            {
                return sourceType.GetProperties().First(p => p.PropertyType == typeof(IList<T>));
            }

            public HierarchicalThing<T>[] Children
            {
                get
                {
                    List<HierarchicalThing<T>> children = new List<HierarchicalThing<T>>();
                    IList<T> childValues = (IList<T>)childrenProperty.GetValue(source, null);
                    foreach (T childValue in childValues)
                    {
                        children.Add(new HierarchicalThing<T>(childValue));
                    }
                    return children.ToArray();
                }
            }

            public override string ToString()
            {
                return source.ToString();
            }
        }
    }
}