# Monday, March 24, 2008

After beginning to use Snooze on a real project I realised it needed to be more extensible and more testable. So I re-wrote it!

The new code is now in the trunk: http://svn2.assembla.com/svn/snooze/trunk

A key extensibility point is the "ResourceContext" class. Before a resource is invoked, it's ResourceContext is initialised with relevant data such as HttpContext, invoke verb, GET view and file type. The base Resource class allows sub-classes to specify a particular ResourceContext sub-class, by the use of a generic type parameter. So when using Snooze in a web project it is easy to define extra context data that is always available to any resources. Instead of adding properties to a Resource class, the use of a context object allows data to be easily shared between a sub-resource and it's parent resource chain.

A Snooze resource also has a ResourceFactory object. The type of this is also generically typed so that a web project can provide a customized factory.

These changes make it easy to do things like test that a resource creates sub-resources (through the factory).

Monday, March 24, 2008 5:19:52 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 

I've seen people testing that RenderView is called by a controller by inheriting into a "testable" controller. Madness!

RenderView is just a convenience method that calls to IViewEngine, so why not just test expectations on that with a mock? We basically want to test that the correct ViewContext is sent to the RenderView method of the view engine.

This is my test:

[TestFixture]
public class HomeControllerTests : ControllerTestsBase<HomeController>
{
    public override HomeController CreateController()
    {
        return new HomeController();
    }

    [Test]
    public void Renders_Index()
    {
        var render = ExpectRenderView();

        Controller.Index();

        Assert.That(render.Data.ViewName, Is.EqualTo("Index"));
    }
}

I don't think we can get more straight forward than that!

This is my test base class:
(I'm using Moq as the mock framework.)

public abstract class ControllerTestsBase<T>
    where T : Controller
{
    public T Controller;
    public Mock<IViewEngine> ViewEngine;

    [SetUp]
    public virtual void SetUp()
    {
        Controller = CreateController();
        ViewEngine = new Mock<IViewEngine>();
        Controller.ViewEngine = ViewEngine.Object;

        Controller.ControllerContext = new Mock<ControllerContext>(new Mock<HttpContextBase>().Object, new RouteData(), Controller).Object;
    }

    public abstract T CreateController();

    public RenderCall<ViewContext> ExpectRenderView()
    {
        var render = new RenderCall<ViewContext>();
        ViewEngine.Expect(v => v.RenderView(It.IsAny<ViewContext>()))
            .Callback(new Action<ViewContext>(render.Set));
        return render;
    }
}

The RenderCall class provides a place to receive the view context that is passed.

public class RenderCall<T>
{
    bool _called;
    T _data;

    public T Data
    {
        get
        {
            if (!_called) throw new InvalidOperationException("View was not rendered.");
            return _data;
        }
    }

    public void Set(T data)
    {
        _data = data;
        _called = true;
    }
}

.net | mvc | thinking | web
Monday, March 24, 2008 4:08:16 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  |