More servicesWindows Live
HomeHotmailSpacesOneCare
 
MSN
Sign in
 
 
Spaces home  Garry Shutler's spaceProfileFriendsBlogMore Tools Explore the Spaces community

Garry Shutler's space

Experiences of a code samurai
There are no categories in use.
No list items have been added yet.
1/29/2008

Moving

I'm moving to http://garryshutler.blogspot.com/ as Live Spaces just doesn't have the features I need. I'm not sure if anyone other than my friend's are subscribing to this, but please change your subscription if you are.

Thanks!

1/26/2008

More fluent assertions using extension methods

I'm a great believer in making your unit tests as easy to read and understand as possible. I was thinking about how I could improve my tests the other day and thought about using extension methods to create a more fluent way of creating complex assertions.

This has allowed me to create a complex assertion like:

  1. testObject.ShouldBeTheSameObjectAs(targetObject).And.ShouldBeEqualTo(testObject).And.ShouldSatisfy(x => x is Object);

Which I hope you'll agree reads a lot easier than those three assumptions on separate lines. Also, note that I have added the ability to use a predicate for when there isn't a method which quite matches your needs.

Here's the code for the extension methods:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using MbUnit.Framework;
  6.  
  7. namespace UnitTestingExtensions
  8. {
  9.     public static class FluentTestingExtensions
  10.     {
  11.         public static FluentAnd<T> ShouldSatisfy<T>(this T testTarget, Predicate<T> predicate)
  12.         {
  13.             Assert.IsTrue(predicate.Invoke(testTarget));
  14.             return new FluentAnd<T>(testTarget);
  15.         }
  16.  
  17.         public static FluentAnd<T> ShouldBeEqualTo<T>(this T testTarget, T comparisonObject)
  18.         {
  19.             Assert.AreEqual(testTarget, comparisonObject);
  20.             return new FluentAnd<T>(testTarget);
  21.         }
  22.  
  23.         public static FluentAnd<T> ShouldBeTheSameObjectAs<T>(this T testTarget, Object comparisonObject)
  24.         {
  25.             Assert.AreEqual(testTarget, comparisonObject);
  26.             return new FluentAnd<T>(testTarget);
  27.         }
  28.  
  29.         public class FluentAnd<T>
  30.         {
  31.             public T And { get; private set; }
  32.  
  33.             public FluentAnd(T target)
  34.             {
  35.                 this.And = target;
  36.             }
  37.         }
  38.     }
  39.  
  40. }

And here are the tests for the extension methods:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using MbUnit.Framework;
  6.  
  7. namespace UnitTestingExtensions.FluentTestingExtensionsTests
  8. {
  9.     [TestFixture]
  10.     public class ShouldSatisfyTests
  11.     {
  12.         [Test, ExpectedException(typeof(MbUnit.Core.Exceptions.AssertionException))]
  13.         public void PredicateEvaluatesAsFalseAndTestFails()
  14.         {
  15.             int testNumber = 4;
  16.  
  17.             testNumber.ShouldSatisfy(x => x > 10);
  18.         }
  19.  
  20.         [Test]
  21.         public void PredicateEvaluatesAsTrueAndTestPasses()
  22.         {
  23.             int testNumber = 4;
  24.  
  25.             testNumber.ShouldSatisfy(x => x == 4);
  26.         }
  27.     }
  28.  
  29.     [TestFixture]
  30.     public class ShouldBeEqualToTests
  31.     {
  32.         [Test, ExpectedException(typeof(MbUnit.Core.Exceptions.AssertionException))]
  33.         public void EqualsCheckFailsAndTestFails()
  34.         {
  35.             int testNumber = 4;
  36.  
  37.             testNumber.ShouldBeEqualTo(7);
  38.         }
  39.  
  40.         [Test]
  41.         public void EqualsCheckPassesAndTestPasses()
  42.         {
  43.             int testNumber = 4;
  44.  
  45.             testNumber.ShouldBeEqualTo(4);
  46.         }
  47.     }
  48.  
  49.     [TestFixture]
  50.     public class ShouldBeTheSameObjectAsTests
  51.     {
  52.         [Test, ExpectedException(typeof(MbUnit.Core.Exceptions.AssertionException))]
  53.         public void DifferentObjectsSoTestFails()
  54.         {
  55.             Object testObject = new Object();
  56.  
  57.             testObject.ShouldBeTheSameObjectAs(new Object());
  58.         }
  59.  
  60.         [Test]
  61.         public void TestPassesWhenTheObjectsAreTheSame()
  62.         {
  63.             Object testObject = new Object();
  64.             Object checkObject = testObject;
  65.  
  66.             testObject.ShouldBeTheSameObjectAs(checkObject);
  67.         }
  68.     }
  69.  
  70.     [TestFixture]
  71.     public class AndTests
  72.     {
  73.         [Test, ExpectedException(typeof(MbUnit.Core.Exceptions.AssertionException))]
  74.         public void TestFailsIfFirstCaseFails()
  75.         {
  76.             int testNumber = 4;
  77.  
  78.             testNumber.ShouldBeEqualTo(3).And.ShouldSatisfy(x => x > 1);
  79.         }
  80.  
  81.         [Test, ExpectedException(typeof(MbUnit.Core.Exceptions.AssertionException))]
  82.         public void TestFailsIfSecondCaseFails()
  83.         {
  84.             int testNumber = 4;
  85.  
  86.             testNumber.ShouldBeEqualTo(4).And.ShouldSatisfy(x => x > 10);
  87.         }
  88.  
  89.         [Test]
  90.         public void TestPassesIfBothCasesPass()
  91.         {
  92.             int testNumber = 4;
  93.  
  94.             testNumber.ShouldSatisfy(x => x > 1).And.ShouldBeEqualTo(4);
  95.         }
  96.  
  97.         [Test]
  98.         public void TestPassesWhenThreeCasesAreUsed()
  99.         {
  100.             Object testObject = new Object();
  101.             Object targetObject = testObject;
  102.  
  103.             testObject.ShouldBeTheSameObjectAs(targetObject).And.ShouldBeEqualTo(testObject).And.ShouldSatisfy(x => x is Object);
  104.         }
  105.     }
  106. }

I'd love to hear some feedback on what you think of this style.

kick it on DotNetKicks.com

1/25/2008

My version of a SmartBag

After seeing Jeffrey Palermo's SmartBag (part 1 and part 2) I was inspired to create my own version which was more specific to my needs. For one I'm a lover of the IEnumerable<T> interface, especially when combined with the power of LINQ for Objects.

Anyway, enough of my waffle, here's the code for the class.

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections;
  4. using System.Text;
  5.  
  6. namespace SmartBag
  7. {
  8.     public class SmartBag
  9.     {
  10.         private Hashtable _hashtable;
  11.  
  12.         public SmartBag()
  13.         {
  14.             this._hashtable = new Hashtable();
  15.         }
  16.  
  17.         public void Add<T>(T item)
  18.         {
  19.             List<T> list;
  20.  
  21.  
  22.             if (!this._hashtable.ContainsKey(typeof(T)))
  23.                 this._hashtable.Add(typeof(T), new List<T>());
  24.  
  25.             list = this._hashtable[typeof(T)] as List<T>;
  26.             list.Add(item);
  27.         }
  28.  
  29.         public IEnumerable<T> Get<T>()
  30.         {
  31.             if (!this._hashtable.ContainsKey(typeof(T)))
  32.                 throw new IndexOutOfRangeException(string.Format("There are no entries for {0}", typeof(T).Name));
  33.  
  34.             foreach (T item in this._hashtable[typeof(T)] as List<T>)
  35.                 yield return item;
  36.         }
  37.     }
  38. }

There are basically two methods to it, Add and Get. Add adds items of the specified type to a list of that type. Get retrieves the list of the specified type.

Below are my test cases which should give you some ideas of how you can use it.

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using MbUnit.Framework;
  6.  
  7. namespace SmartBag
  8. {
  9.     [TestFixture]
  10.     class SmartBagTest
  11.     {
  12.         [Test]
  13.         public void AClassAddedAgainstAnInterfaceCanBeRetrievedTogether()
  14.         {
  15.             SmartBag bag = new SmartBag();
  16.  
  17.  
  18.             bag.Add<IContract>(new Parent());
  19.             bag.Add<IContract>(new Child());
  20.  
  21.             Assert.AreEqual(2, bag.Get<IContract>().Count());
  22.         }
  23.  
  24.         [Test]
  25.         public void AChildWillBeAddedToItsParentsEnumerationIfYouDefineIt()
  26.         {
  27.             SmartBag bag = new SmartBag();
  28.  
  29.  
  30.             bag.Add(new Parent());
  31.             bag.Add<Parent>(new Child());
  32.  
  33.             Assert.AreEqual(2, bag.Get<Parent>().Count());
  34.         }
  35.  
  36.         [Test, ExpectedException(typeof(IndexOutOfRangeException))]
  37.         public void AnExpectionWillBeRaiseWhenAttemptingToRetrieveATypeThatDoesNotExist()
  38.         {
  39.             SmartBag bag = new SmartBag();
  40.  
  41.  
  42.             foreach (Object item in bag.Get<Object>()) { } // must enumerate for it to be evaluated
  43.         }
  44.  
  45.         [Test]
  46.         public void ShouldBeAbleToAddChildAsADifferentType()
  47.         {
  48.             SmartBag bag = new SmartBag();
  49.  
  50.  
  51.             bag.Add(new Parent());
  52.             bag.Add(new Child());
  53.  
  54.             Assert.AreEqual(1, bag.Get<Parent>().Count());
  55.             Assert.AreEqual(1, bag.Get<Child>().Count());
  56.         }
  57.  
  58.         private class Parent : IContract
  59.         {
  60.         }
  61.  
  62.         private class Child : Parent
  63.         {
  64.         }
  65.  
  66.         private interface IContract
  67.         {
  68.         }
  69.     }
  70. }

To begin with this is a basic idea. I hope to utilise it in an upcoming project and it will probably look a bit different after I've used it in anger. Something I can see I might need is to extract an interface to make testing easier but I've left that out for now as YAGNI may apply.

Any feedback would be greatly appreciated.

kick it on DotNetKicks.com

12/12/2007

Mocking out the IHttpContext to test your routing rules

Now that we can get hold of the ASP.NET MVC framework we can start playing with it and of course writing tests to check our applications work.
 
In Scott Gu's post on routing rules he shows how you can test your routing rules using a MockContext object. Unfortunately this is not available as I write this but I needed to check my routing rules were working properly today as I was having problems implementing MVC into a project. After a little bit of nosing about with Reflector and using unexpected calls in my tests to guide me I got my test running. The following is code to test your routing rules are working as you expect using MbUnit and RhinoMocks.
 
The method in my Global.asax defining my routing rules:
 
Public Sub AddRoutingRules(ByRef routes As RouteCollection)
    routes.Add(New Route("Report/[reportname]/[action]/[id]", New With {.controller = "Report"}, GetType(MvcRouteHandler)))
    routes.Add(New Route("Report/[reportname]/[action]", New With {.controller = "Report"}, GetType(MvcRouteHandler)))
    routes.Add(New Route("Report/[reportname]", New With {.controller = "Report", .action = "Display"}, GetType(MvcRouteHandler)))
    routes.Add(New Route("[controller]/[action]", GetType(MvcRouteHandler)))
    routes.Add(New Route("[controller]", New With {.action = "Index"}, GetType(MvcRouteHandler)))
    routes.Add(New Route("Default.aspx", New With {.controller = "Default", .action = "Index"}, GetType(MvcRouteHandler)))
End Sub
The code to test the routing works correctly:

<Test()> _
Public Sub Check_that_the_routing_tables_direct_to_the_report_controller_correctly()
    Dim application As New Global_asax
    Dim routes As New RouteCollection
    Dim mockery As New MockRepository
    Dim context As IHttpContext
    Dim request As IHttpRequest
    Dim route As RouteData

    context = mockery.CreateMock(Of IHttpContext)()
    request = mockery.CreateMock(Of IHttpRequest)()
    application.AddRoutingRules(routes)

    Using mockery.Record
        SetupResult.For(context.Request).Return(request)
        SetupResult.For(request.AppRelativeCurrentExecutionFilePath).Return("~/")
        SetupResult.For(request.PathInfo).Return("Report/SomeReport/Wholesaler/1")
    End Using

    Using mockery.Playback
        route = routes.GetRouteData(context)
    End Using

    Assert.AreEqual("Report", route.Values("controller"))
    Assert.AreEqual("SomeReport", route.Values("reportname"))
    Assert.AreEqual("Wholesaler", route.Values("action"))
    Assert.AreEqual("1", route.Values("id"))
End Sub

Hope this saves someone a bit of time.

By the way, I'm new to this blogging thing so if someone could point me in the direction of a nice code formatter it would be appreciated.

Edit: around the same time I published this Thomas Gravgaard wrote a similar post but in C#.

kick it on DotNetKicks.com