Saturday, 17 October 2009

Unit Testing Abstract Classes

I'm doing some development at the moment in order to sharpen my skills and I'm mostly playing around with TDD. Part of my solution involves two concrete classes which share some common functionality and therefore via the magic of refactoring inherit from the same abstract base class. I knew from the start that I'd end up with such a base class but because I wanted to stick rigidly to letting my tests define my classes I didn't 'discover' the base class until one of the subclasses was mostly written.

So refactoring my concrete class means moving some functionality into the new base class, does this also means that I should also move the related tests into a new test fixture? No, not move them anyway; the tests should remain where they are in the concrete class test fixture since its contract hasn't changed in the refactoring, only the implementation has changed. However I should still test the base class, with what essentially will be a copy and paste of the tests in the concrete class.

So now I have two sets of near identical tests that test essentially same thing. Remembering that I mentioned above that there are two subclasses this now becomes three sets of the same tests. So now I think I can be sly and do some refactoring of my tests to avoid duplication. I create a base test class from which all three tests fixtures inherit (a-la a solution proposed by Mark Needham), in this base test class I can put all the common tests. Done.

Though this works swimmingly I can't shake the feeling that I'm heading down the wrong path. Sure now I have a scalable solution for testing X number of subclasses but in doing so I've had to couple my unit tests together, they now know about each other and dependencies exist. The solution works, but only if the subclasses stick to the same contract as the parent and do not try to override behaviour.

I firmly believe that unit tests should test code in as much isolation as possible so I'm thinking now that maybe I should have three separate test fixtures again. My tests are the contract that my code needs to fulfill in order to be accepted, and in that sense I have three separate contracts anyway. Sure the contracts look the same, just the names change, but the crucial thing I gain is I can change the terms and conditions on one without invalidating my others.

Perhaps I'm being overly pedantic, but the duplication sits easier with me than the inheritance model. I'd be interested in what others think of this as I know I'm probably in a minority here.

Thursday, 8 October 2009

Masterpages, Viewdata, and MVC

Preamble

A while ago I took a lull at work as an excuse to muck around with the then new (ish) ASP Mvc Framework. I'd read alot about it, investigated it for a bit, borrowed ideas from it for existing projects, and been disapointed when it arrived just too late to be adopted for a major project I became embroiled in. To me the Mvc Framework was the magic bullet I was praying for the slay the evils of Franken-Postback and Count Business-Logic-In-Gui-Code-ula. It worked; it was easy; it was peasy; it was lemon-squeezey. Everything was going great until I tried to tie masterpages to my viewdata.

The situation was this: I had a masterpage that I wanted to display data controlled by a simple CMS (for CMS read web.config file - this was a prototype afterall), and I wanted said data to be fetched using the same Mvc pattern that data bound in regular pages is; in short I wanted my controllers to get data that the masterpage would use.

As always my good friend google was the first port of call and while I found several good solutions none were as neat as I'd like. The best solution (in my mind) was offered by Mike Linnen, he basically proposed defining a base Viewdata class which contained the masterpage data - all concrete viewdata would inherit from this. This solution defined a controller that fetched the base Viewdata and would be called from every other controller. I liked this solution but it had two things that made me seek another way:

  1. You had to explicitly call the base controller from your concrete controller.
  2. If you wanted to have a page that rendered say a simple IList<string> then you'd have to define a specific class for that purpose.
So using this idea as a base I came up with my own solution.

My Way My way invloves defining our very own Viewdata class; said Viewdata class is setup to contain our masterpage data as well as any other data required for a subpage. A base controller (from which all controllers should inherit) then ensures that this base Viewdata class is used at all times. Rather than try and explain I'll post some code.

BaseViewdata.cs
    
   /// <summary>
   /// Untyped base view data class - for use in the masterpage
   /// </summary>
   public class BaseViewdata
   {
       #region Properties

       /// <summary>
       /// The Strapline for the page.
       /// </summary>
       public virtual string Strapline
       {
           get;
           set;
       }

       #endregion
   }

   /// <summary>
   /// Typed base view data class - for use in pages.
   /// </summary>
   public class BaseViewdata<T> : BaseViewdata
   {
       #region Properties

       /// <summary>
       /// The typed data contained by the view data.
       /// </summary>
       public virtual T Data
       {
           get;
           set;
       }

       #endregion
   }

The BaseViewdata class comes in two flavours: typed, and untyped. We have both because when coding the masterpage we won't know what kind of BaseViewdata we're using - whether it is BaseViewdata or BaseViewdata> etc - for this reason, and the fact that the masterpage shouldn't care about the page specific data, there is an untyped varient. The untyped BaseViewdata will contain all the masterpage specific properties - in this case the strapline text to display at the top of the page.

The typed viewdata simply acts as a wrapper around the data to be used by the page view.

BaseController.cs
   /// <summary>
   /// Base controller for the site.
   /// </summary>
   /// <remarks>
   /// This class acts as a base controller for all views that uses the Site.Master masterpage.
   /// </remarks>
   public abstract class BaseController : Controller
   {
       #region Fields

       /// <summary>
       /// Object used for synchronisation.
       /// </summary>
       private readonly object syncObj = new object();

       /// <summary>
       /// The base viewdata for the site.
       /// </summary>
       private BaseViewdata baseData;

       #endregion

       #region Properties

       /// <summary>
       /// The base viewdata for the site.
       /// </summary>
       public BaseViewdata BaseData
       {
           get
           {
               if (this.baseData == null)
               {
                   lock (this.syncObj)
                   {
                       if (this.baseData == null)
                       {
                           PopulateBaseData();
                       }
                   }
               }
               return this.baseData;
           }
       }

       #endregion

       #region Methods

       /// <summary>
       /// Returns a System.Web.Mvc.ViewResult that renders a view to the response.
       /// </summary>
       /// <typeparam name="T">
       /// The type of data contained in the viewdata.
       /// </typeparam>
       /// <param name="data">
       /// The data contained in the viewdata.
       /// </param>
       /// <returns>
       /// The ViewResult that is to be rendered as a view.
       /// </returns>
       protected virtual ActionResult View<T>(T data)
       {
           ActionResult result;

           var viewData = CreateViewdata(data);
           result = base.View(viewData);

           return result;
       }

       /// <summary>
       /// Returns a System.Web.Mvc.ViewResult that renders a view to the response.
       /// </summary>
       /// <typeparam name="T">
       /// The type of data contained in the viewdata.
       /// </typeparam>
       /// <param name="viewName">
       /// The name of the view to render.
       /// </param>
       /// <param name="data">
       /// The data contained in the viewdata.
       /// </param>
       /// <returns>
       /// The ViewResult that is to be rendered as a view.
       /// </returns>
       protected ActionResult View<T>(
           string viewName,
           T data)
       {
           ActionResult result;

           var viewData = CreateViewdata(data);
           result = base.View(
               viewName,
               viewData);

           return result;
       }

       /// <summary>
       /// Returns a System.Web.Mvc.ViewResult that renders a view to the response.
       /// </summary>
       /// <typeparam name="T">
       /// The type of data contained in the viewData.
       /// </typeparam>
       /// <param name="viewName">
       /// The name of the view to render.
       /// </param>
       /// <param name="masterName">
       /// The name of the view master.
       /// </param>
       /// <param name="data">
       /// The data contained in the viewData.
       /// </param>
       /// <returns>
       /// The ViewResult that is to be rendered as a view.
       /// </returns>
       protected ActionResult View<T>(
           string viewName,
           string masterName,
           T data)
       {
           ActionResult result;

           var viewData = CreateViewdata(data);
           result = base.View(
               viewName,
               masterName,
               viewData);

           return result;
       }

       /// <summary>
       /// Returns a System.Web.Mvc.ViewResult that renders a view to the response.
       /// </summary>
       /// <param name="data">
       /// The data contained in the viewdata.
       /// </param>
       /// <returns>
       /// The ViewResult that is to be rendered as a view.
       /// </returns>
       protected new ActionResult View(object data)
       {
           return this.View<object>(data);
       }

       /// <summary>
       /// Returns a System.Web.Mvc.ViewResult that renders a view to the response.
       /// </summary>
       /// <param name="data">
       /// The data contained in the viewdata.
       /// </param>
       /// <param name="viewName">
       /// The name of the view to render.
       /// </param>
       /// <returns>
       /// The ViewResult that is to be rendered as a view.
       /// </returns>
       protected new ActionResult View(
           string viewName,
           object data)
       {
           return this.View<object>(
               viewName,
               data);
       }

       /// <summary>
       /// Returns a System.Web.Mvc.ViewResult that renders a view to the response.
       /// </summary>
       /// <param name="data">
       /// The data contained in the viewdata.
       /// </param>
       /// <param name="viewName">
       /// The name of the view to render.
       /// </param>
       /// <param name="masterName">
       /// The name of the view master.
       /// </param>
       /// <returns>
       /// The ViewResult that is to be rendered as a view.
       /// </returns>
       protected new ActionResult View(
           string viewName,
           string masterName,
           object data)
       {
           return this.View<object>(
               viewName,
               masterName,
               data);
       }

       #endregion

       #region Private methods

       /// <summary>
       /// Populate the base viewdata for the site.
       /// </summary>
       private void PopulateBaseData()
       {
           this.baseData = new BaseViewData()
           {
               Strapline = "The Snapping Turtles Are Massing!"
           };
       }

       /// <summary>
       /// Creates the viewdata that will be passed to the page.
       /// </summary>
       /// <remarks>
       /// Pulls in all non-generic values from the base viewdata.
       /// </remarks>
       /// <typeparam name="T">
       /// The type of data contained in the viewdata.
       /// </typeparam>
       /// <param name="data">
       /// The data contained in the viewdata.
       /// </param>
       /// <returns>
       /// The created viewdata.
       /// </returns>
       private BaseViewdata<T> CreateViewdata<T>(T data)
       {
           BaseViewdata<T> result;

           result = new BaseViewdata<T>()
           {
               Data = data,
               Strapline = this.BaseData.Strapline,
           };

           return result;
       }

       #endregion
   }

The BaseController basically works by defining generic versions of the existing View methods and shadowing (we can't override them unfortunately) the existing versions. The new methods work in exactly the same way as the old methods but with one added step: they take the data provided and copy it into a BaseViewdata instance which is then passed to the original View method - ie: we're wrapping the data using our BaseViewdata class.

One thing to point out is that, in this code. the BaseViewdata is created when needed, therefore it is available to the controller at any point allowing you to override settings if and when needed.

Putting these two fellas into action we can create a page/masterpage combo that might look a bit like this:

Site.Master
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<BaseViewdata>" %>

<html xmlns="http://www.w3.org/1999/xhtml" >
   <head runat="server">
       <title>About a year too late Andy!</title>
   </head>
   <body>
      
  <div class="strapline">
   <%= this.ViewData.Model.Strapline %>
  </div>
  
  <asp:ContentPlaceHolder ID="MainContent" runat="server">
  
  </asp:ContentPlaceHolder>     
      
   </body>
</html>
Index.aspx
<%@ Page Title="" Language="C#" MasterPageFile="Site.Master" Inherits="System.Web.Mvc.ViewPage<BaseViewdata<string>>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="Content" runat="server">
 Your Viewdata contains the string <%= this.ViewData.Model.Data %>!
</asp:Content>
Hooray!

And there you go, masterpages with viewdata in a scaleable solution - all you have to do is make sure your controllers inherit from the BaseController and you'll be fine. The only things about this solution that I find distastefull are:

  1. You have to shadow methods rather than overriding them, but this can't be helped and it only affects untyped data anyway.
  2. Your code-behind now contains the rather ugly System.Web.Mvc.ViewPage<BaseViewdata<string>>. If you really want to make this neater then you can always subclass the ViewPage class so that the BaseViewData part becomes implied/forced.

Sunday, 4 October 2009

Welcome

I've decided to throw my hat into the ring and start my own tech blog. Now I know tech blogs are ten-a-penny (my 'research' shows that blog sites are second only to porn in number) so why bother creating another one? I'll tell you why:

  • Very occasionally I do something for myself sans-copy-and-paste that could genually help others in the way other blog posts have helped me.
  • I get to complain about Windows Workflow in a public environment.
  • It serves as a handy lookup for me so in the future I can look at my posts and say to myself "ah so that's how you do apostrophes in XHTML that work with IE6".
  • It looks good on my CV and may help me find a job that isn't in Burger King.

So welcome to my blog: Don't Worry, It's In-flammable, or DWIIF as all the cool kids call it. The name has nothing to do with software what-so-ever but is a Simpsons quote (Dr Nick Riviera) that makes me smile; no doubt this will be the first of many such references.

The plan is to get some actual content up soon once I've got the site design bit done - this post acting as some dummy content that I can style offline. See you soon.