News

August 2009 Entries

Unit Testing and approval tests


It's been years now that unit testing frameworks and tools have grabbed our attention, made their way into our favorite IDE and sparked yet another wave of seemingly endless "my framework is better than yours" wars. And then there are the principal wars of whether TDD is better than Test After Development. And most excitingly the automated testing tools etc. Oh, and let's not forget mocks! So we have all the tools we need – right? Well, kind of, no.

I recently attended a talk by Llewellyn Falco and Woody Zuill the other day, and they used a little library called Approval Tests (http://approvaltests.com/). I immediately fell in love with the concept and the price ($0). Llewellyn is one of the two developers of the product, hosted on http://sourceforge.net/projects/approvaltests/.

What does it do that years of frameworks don't?

For me, a major nuisance is that you have a piece of code, and that piece of code produces a result (object, state – call it what you will). That result is commonly named "actual" in Visual Studio unit tests. The developer must then assert against that result to ensure that the code ran properly. In theory, unit tests are supposed to be small and only test one thing. In reality, functions which return complex objects rarely can be asserted with one assert. You'd typically find yourself asserting several properties or testing some behavior of the actual value. In corporate scenario, the unit tests might morph into some degree of integration tests and objects become more complex, requiring a slew of assert.this and assert.that().

What if you could just run the code, inspect the value returned, and – if it satisfies the requirements – set "that value" to be the comparison for all future runs? Wouldn't that be nice? Good news: Approval Tests does just that. So now, instead of writing so many asserts against your returned value, you just call ApprovalTests.Approve() and the captured state is compared against your current runtime. Neat huh?

But wait, there's more! What if your requirements are regarding a windows form, or a control? how do you write a unit test that asserts against a form? The answer is simple and genius: Capture the form as an image, and compare the image produced by your approved form (after you have carefully compared it to the form given to you by marketing – right?) and compare it to each subsequent run. ApprovalTests simply does a byte by byte comparison of the resultant picture of the form and tells you if it matches what you approved.

Let's break down the steps for installation:

  1. Add a reference to the ApprovalTests.dll assembly.

Yeah,it's that simple.

Ok. Let's break down the steps for adding an approval to your unit test

  1. Write your unit test (arrange, act – no asserts) as usual.
  2. Add a call to Approve()

Yeah, it's that simple.

How do you tell it that the value received is "the right one"? Upon first run, the Approve() call will always fail. Why? Because it has no stored approved value to compare the actual it received against. When it fails, it will print out a message (console or test report – depends on your unit test runner). That message will contain a path for the file that it received, complaining about a lack of approved file. The value captured from that run (and any subsequent run) is stored in a file named something like "{your unit test file path}\{your method}.received.xyz". If you like the result of the run -the image matches what the form should look like, or the text value is what your object should contain etc – then you can rename it to "{your unit test file path}\{your method}.approved.xyz". You should check the approved file into your source control. After all, it's content constitutes the basis of the assertion in your unit test!

Consider the very simple unit test code:

   1: [Test]
   2:         public void DateTime_Constructor_YMDSetDate()
   3:         {
   4:             DateTime actual;
   5:             actual = new DateTime(2038, 4, 1);
   6:             Approvals.Approve(actual.ToString());
   7:         }

The function under test here is the constructor for System.DateTime which takes 3 parameters – month day and year. Upon first run, the test will fail with a message resembling

"TestCase 'MyProject.Tests.DateTimeTest.DateTime_Constructor_YMDSetDate'
failed: ApprovalTests.Core.Exceptions.ApprovalMissingException : Failed Approval: Approval File "C:\….\MyProject\DateTimeTest.DateTime_Constructor_YMDSetDate.approved.txt" Not Found."

That's because this file was never created – it's your first run. Using the standard approver, this file will actually now be created but will be empty. In that same directory you will find a file named {…}.received.txt. This is the capture from this run. You can open the received value file, and inspect the text to ensure that the value returned matches your expected value. If it does, simply rename that file to .approved.text or copy it's content over and run the test again. This time, the Approve() method should succeed. If at any point in the future the method under test returns a different value, the approval will fail. If at any point in the future the specs change, you will need to re-capture a correct behavior and save it.

How do you easily compare the values from the last run and the saved approved value? The approved file is going to be a text file for string approvals, and an image file for WinForm approvals. As it turns out, you can instruct Approval Tests to launch a diff program with the 2 files (received, approved) automatically upon failure, or just open the received file upon failure or silently just fail the unit test. To control that behavior, you use a special attribute

   1: [TestFixture]
   2: [UseReporter(typeof(DiffReporter))]
   3: public class ObjectWriterTest
   4: {

The UserReporterAttribute allows you to specify one of 3 included reporters

  1. DiffReporter – which opens tortoisediff for diffing the received and approved files if the comparison fails.
  2. OpenReceivedFileReporter – which launches the received file using the application registered to it's extension on your system if the comparison fails.
  3. QuietReporter – which does not launch any program but only fails the unit test if the comparison fails.

When your have a CI server and run unit tests as part of your builds, you probably want to use the quiet reporter. For interactive sessions, one of the first 2 will probably be more suitable.

How are value comparisons performed? Under the standard built in methods, a file is written out and compared to byte by byte. If you wish to modify this or any behavior, you can implement your own approver or file writer or reporter by implementing simple interfaces. I ended up adding a general use object writer so that I can approve arbitrary objects. The effort was fairly painless and straightforward.

I did have to read the code to get the concept. If only my time machine worked: I could have read my own blog and saved myself 20 minutes. Yeah, right.

The project has some bells and whistles – plug ins for a few IDE's and there's a version for Java and Ruby. I have not reviewed these versions.

There you have it folks- a shiny new tool under your belt. I can see this saving my hours of mindless typing of Assert.* calls and I can go home early. Yeah, right.

posted @ Saturday, August 08, 2009 1:13 AM | Feedback (0) |


MVC Radio Button List


As predicted, I came around to using some radio  buttons. As you might guess by now, I didn't like the HTML or the implementation in the current MVC release. As you may expect, I wrote my own :-)

The implementation is fairly simple and straightforward. It extends System.Web.MVC.ViewPage, takes a list of objects, allows for selection of one of the radio buttons, supports orientation and supports selection of both the value the radio button submits and the display string for that item independently.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq.Expressions;
   4: using System.Web.Mvc;
   5: using System.Web.UI;
   6:  
   7: public static partial class HtmlHelpers
   8: {
   9:     public static void ShowRadioButtonList<T>(this ViewPage page, IList<T> list, string name, Expression<Func<T, object>> valueProperty, Expression<Func<T, object>> displayProperty, System.Web.UI.WebControls.Orientation orientation)
  10:     {
  11:         page.ShowRadioButtonList(list, name, valueProperty, displayProperty, "3", orientation);
  12:     }
  13:  
  14:  
  15:     public static void ShowRadioButtonList<T>(this ViewPage page, IList<T> list, string name, Expression<Func<T, object>> valueProperty, Expression<Func<T, object>> displayProperty, string selectedValue, System.Web.UI.WebControls.Orientation orientation)
  16:     {
  17:         HtmlTextWriter writer = new HtmlTextWriter(page.Response.Output);
  18:         if (writer != null)
  19:         {
  20:             Func<T, object> valueGetter = valueProperty.Compile();
  21:             Func<T, object> displayStringGetter = displayProperty.Compile();
  22:  
  23:             for (int i = 0; i < list.Count; i++)
  24:             {
  25:                 T row = list[i];
  26:                 string value = valueGetter(row).ToString();
  27:                 writer.AddAttribute(HtmlTextWriterAttribute.Type, "radio");
  28:                 writer.AddAttribute(HtmlTextWriterAttribute.Id, name + "_" + i);
  29:                 writer.AddAttribute(HtmlTextWriterAttribute.Name, name, true);
  30:                 writer.AddAttribute(HtmlTextWriterAttribute.Value, value, true);
  31:                 if (value == selectedValue)
  32:                 {
  33:                     writer.AddAttribute(HtmlTextWriterAttribute.Checked,"checked");
  34:                 }
  35:                 writer.RenderBeginTag(HtmlTextWriterTag.Input);
  36:                 writer.Write(displayStringGetter(row));
  37:                 writer.RenderEndTag();
  38:  
  39:                 if (orientation == System.Web.UI.WebControls.Orientation.Vertical)
  40:                 {
  41:                     writer.RenderBeginTag(HtmlTextWriterTag.Br);
  42:                     writer.RenderEndTag();
  43:                 }
  44:             }
  45:         }
  46:     }
  47: }

 

This implementation uses the type Expression<Func<T,Object>> for the selection of the value and the display string. To invoke the helper, assuming you have a list of Product objects, Product being defined:

   1: public class Product
   2: {
   3:     public int ProductID { get; set; }
   4:     public string Title { get; set; }
   5:     public string Description { get; set; }
   6:     public decimal Price { get; set; }
   7: }

You can now call the helper from your MVC View Page strongly typed with a model List<Product>:

   1: <%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<List<Product>>"
   2:     MasterPageFile="~/Views/Shared/Site.Master" %>
   3:  

invoked simply

   1: <% this.ShowRadioButtonList<Product>(Model, 
   2:                             "productRadioButtonList", 
   3:                             a => a.ProductID, 
   4:                             d => d.Title, "4", 
   5:                             Orientation.Horizontal); %>

This call will create a radio button for each product in the list (Model is a List<Product> because we strongly typed the page as such). The radio button will use the ProductID property for the value of the input, and the Title property as the displayed string. If one of the product Id's happens to be 4, that radio button will be selected in the group. The radio buttons will all be named "productRadioButtonList" but their ID will be appended an ordinal number  "productRadioButtonList_1","productRadioButtonList_2","productRadioButtonList_3" etc.

There you have it: another day, another MVC control, another blog post.

posted @ Wednesday, August 05, 2009 7:29 PM | Feedback (2) |


MVC paged list


The need

As certain as the sun rising tomorrow, there will come the point where you will want to display a list or grid with paging. While many solutions exist, and many component developers are coming in with robust solutions, a simple and satisfactory solution can be created fairly easily.

Implementation

Why create a pager from scratch? Several reasons:

1) You want to control the pager completely – display, style and all.

2) You don't like the idea of JavaScript paging, which will load your hundreds of pages to the browser and do client side paging / grid

3) You want to understand and control exactly how a page subset of record is fetched and take control of database or IO thrashing

Of the quickly surveyed solutions out there, I found this one simple and straightforward. Being small, straightforward and simple means also easy to maintain, extend or modify. Based on that solution, I've created my own pager which breaks into 2 class implementations and one usage guidance.

The first class, is the PagedList class. The whole class is rather small and the only crux is doing correct math and ensuring the logic handles zero items returned. This class is responsible for taking a source list of all items (more on that below in the performance considerations) and presenting simple properties for HasNextPage, HasPreviousPage, TotalPages and CurrentPage. The implementation inherits from the generic List<T>, and so exposes and enumerator and the Count property. The constructor copies only the current page's worth of items into the instance though, so Count will return the number of items on the current page (0 to page size) therefore an additional property TotalItems is populated upon construction which exposes the total number of items in the underlying source.

using System;
using System.Collections.Generic;
using System.Linq;

namespace BL.Models
{

    /// <summary>
    /// Adapted from http://blog.wekeroad.com/2007/12/10/aspnet-mvc-pagedlistt/
    /// </summary>
    /// <typeparam name="T">The type of item this list holds</typeparam>
    public class PagedList<T> : List<T>, IPagedList
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="PagedList&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="source">The source list of elements containing all elements to be paged over.</param>
        /// <param name="currentPage">The current page number (1 based).</param>
        /// <param name="pageSize">Size of a page (number of items per page).</param>
        public PagedList(IEnumerable<T> source, int currentPage, int itemsPerPage)
        {
            this.TotalItems = source.Count();
            this.ItemsPerPage = itemsPerPage;
            this.CurrentPage = Math.Min(Math.Max(1, currentPage), TotalPages);
            this.AddRange(source.Skip((this.CurrentPage - 1) * itemsPerPage).Take(itemsPerPage).ToList());
        }


        public int CurrentPage {get ;private set;}

        public int ItemsPerPage { get; private set; }

        public bool HasPreviousPage { get { return (CurrentPage > 1); } }

        public bool HasNextPage { get { return (CurrentPage * ItemsPerPage) < TotalItems; } }

        public int TotalPages { get { return (int)Math.Ceiling((double)TotalItems / ItemsPerPage); } }

        public int TotalItems { get; private set; }
    }
}

PagedList implements the interface IPagedList, since a static class can not be generic, and the control renderer will need access to the PagedList's properties:

   1: using System;
   2:  
   3: namespace BL.Models
   4: {
   5:     public interface IPagedList
   6:     {
   7:         int CurrentPage { get; }
   8:         bool HasNextPage { get; }
   9:         bool HasPreviousPage { get; }
  10:         int ItemsPerPage { get; }
  11:         int TotalItems { get; }
  12:         int TotalPages { get; }
  13:     }
  14: }

The second class is more like a custom web control. Since this is MVC, we are driven to use a helper like implementation. My approach to developing HTML helpers for MVC is to create an extension method on the System.Web.MVC.ViewPage type. This allows the use of the well known and tested HtmlTextWriter to render the actual HTML rather than creating angled brackets in strings on the fly. I find this approach both more true to the form – rendering output to the output stream and not composing a string to be copied later – and safe: using compliant well tested constants and constructs rather than typing in HTML and hoping your syntax and understanding of the tag is correct.

using System.Web.Mvc;
using System.Web.UI;

public static partial class HtmlHelpers
{
    /// <summary>
    /// Shows a pager control - Creates a list of links that jump to each page
    /// </summary>
    /// <param name="page">The ViewPage instance this method executes on.</param>
    /// <param name="pagedList">A PagedList instance containing the data for the paged control</param>
    /// <param name="controllerName">Name of the controller.</param>
    /// <param name="actionName">Name of the action on the controller.</param>
    public static void ShowPagerControl(this ViewPage page, IPagedList pagedList, string controllerName, string actionName)
    {
        HtmlTextWriter writer = new HtmlTextWriter(page.Response.Output);
        if (writer != null)
        {
            for (int pageNum = 1; pageNum <= pagedList.TotalPages; pageNum++)
            {
                if (pageNum != pagedList.CurrentPage)
                {
                    writer.AddAttribute(HtmlTextWriterAttribute.Href, "/" + controllerName + "/" + actionName + "/" + pageNum);
                    writer.AddAttribute(HtmlTextWriterAttribute.Alt, "Page " + pageNum);
                    writer.RenderBeginTag(HtmlTextWriterTag.A);
                }

                writer.AddAttribute(HtmlTextWriterAttribute.Class,
                                    pageNum == pagedList.CurrentPage ?
                                                        "pageLinkCurrent" :
                                                        "pageLink");

                writer.RenderBeginTag(HtmlTextWriterTag.Span);
                writer.Write(pageNum);
                writer.RenderEndTag();

                if (pageNum != pagedList.CurrentPage)
                {
                    writer.RenderEndTag();
                }
                writer.Write("&nbsp;");
            }

            writer.Write("(");
            writer.Write(pagedList.TotalItems);
            writer.Write(" items in all)");
        }
    }
}

 

The implementation creates a list of page numbers, with a link on each except for the current page. The link will be of the format "/{controller name}/{action name}/{page number}".

I have added conditional style attribute to the link so that you can style the current page differently from the other pages easily. Since you have the code, you can extend the resultant HTML as you wish. You might want to have text indication of "no more pages" or some indication if the list is empty etc.

Finally, you would want to make use of this shiny new widget. The steps are as follows

1) In your controller, create an action which takes the page number as it's sole parameter. The action would then create a new instance of the PagedList, passing it the "full list" and the current page number from the parameter.

public ActionResult Page(int id)
        {
            List<Product> products = CatalogService.ListOpenProducts();
            PagedList<Product> data = new PagedList<Product>(products, id, PAGE_SIZE);
 
            return View(data);
        }

2) Change / create a view which takes the PagedList<your row type> as it's model.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<BL.Models.PagedList<Product>>" %>

3) Place a call to the extension method to display the pager anywhere in your view (multiple placement allowed – you can put one on top and one on the bottom etc). Recall that the method ShowPagerControl() extends  ViewPage, so the keyword this should show your intellisence for the method. If you chose a more complex model (MVVM ViewModel containing more data than just the paged list) then you would use Model.{paged list property name}. The use of the view as a bag of random data conjured up by string names should IMHO be universally abandoned and eliminated.

<% this.ShowPagerControl(Model, "Bids", "Page"); %>

 

Considerations

Take != load all + scroll

The PagedList implementation takes an IEnumerable<T> as it's source data. Internally, it uses Linq syntax which would seem to require all items be loaded, and then skip the first N pages and take the next {page size} worth of items. If your underlying list of items is a huge DB call, you will find that troubling. What you might consider then will be to use deferred loading. Extend the Linq IQueryable<T> or ObjectQuery<T> and let the ORM of your choice do the paging in the database. If your ORM is eager loader, you will need to implement custom partial record loading and paging at the data source level. If it can defer loading you will be in better shape.

Conversely, you might want to actually eager-load all records at the first shot. This will provide you with 2 benefits: cachability and coherency. Loading all items into memory incurs one DB call overhead and the IO required for all records. If you load each page at a time, you would incur {page count} * page clicks DB call overheads which might exceed the former if users scroll often back and forth. Once you load the whole list, you can cache it in memory and expire it based on data change events. If you have the base list in memory, access to it incurs no more IO regardless of pager clicks. Another phenomena caching gets around is coherency problems. If you page ad the DB level and an item is inserted or deleted, the end user experiences skips or stutter items. A skip is when a user clicks from page 1 to 2 an item which was to be on page 2 now is in the range of page 1 because an item on page 1 was deleted. Going to page 2 skips this item, and paging back should reveal it but would be surprising to the user (because she just came from page 1 and it wasn't there before) creating the appearnace of a skipped / missed item. A stutter is the reverse situation: user clicks from page 1 to 2, and an item from page 1 appears again on page 2. This happens when an item was added and "pushed" the repeat item into page 2 because of it's sorting order. This appears to the user as a malfunction and may infuriate some enough to call customer service (alas, advising customers to adjust their medication does not actually calm them down). A solution to coherence is to cache the result list for each user, expiring the cache actively when navigating away or running a different query.

Conclusion

The code above and variations of it are fairly easy to create. If your favorite web control vendor has not solved this for you, if you want to take full control of your paging of if you are just naturally curious – it's a great way to add paging to your MVC application.

posted @ Wednesday, August 05, 2009 2:16 PM | Feedback (4) |