Thursday, February 26, 2009

Fluent interface for argument validation

I was fiddling around a bit with a fluent interface for validating method-arguments, it looks kind of promising…

    Public Class Foo
        Public Sub New(ByVal bar As String, ByVal baz As Integer)
            Ensure.ThatArgument(bar).IsNotNull()
            Ensure.ThatArgument(baz).IsInRange(1, 10)

            ' The argument can be named to for better exception messages...
            Ensure.ThatArgument(bar).Named("bar").IsNotNull()
            Ensure.ThatArgument(baz).Named("baz").IsInRange(1, 10)
        End Sub
    End Class

Using generics also makes the intellisense really helpful, for example the “IsInRange”-validation only shows up for IComparable(T)-types.

Thursday, February 12, 2009

String concatenation made easy

How many times have you written code similar to this?

   1: var builder = new StringBuilder();
   2:  
   3: foreach (string foo in bar)
   4: {
   5:     builder.Append(foo);    
   6: }
   7:  
   8: return builder.ToString();

I have hopefully done it for the last time since I created a couple of extension methods for IEnumerable<string>, well actually, one method with a couple of overloads.

The first one is really simple and just concatenates all the strings in the specified collection to a single string, something like:

   1: var sequence = new string[] { "foo", "bar" };
   2: string result = sequence.Concatenate();

In this case the result variable will contain the string “foobar”. One of the overloads will let you specify a separator:

   1: var sequence = new string[] { "foo", "bar" };
   2: string result = sequence.Concatenate(", ");

Now the result is “foo, bar”.

And the last one will let you specify a prefix and a suffix in addition to the separator:

   1: var sequence = new string[] { "foo", "bar" };
   2: string result = sequence.Concatenate(", ", "(", ")");

Giving you the result “(foo, bar)”. Quite neat!

Here’s the code for the extensions:

   1: /// <summary>
   2: /// Concatenates all the strings in the specified sequence.
   3: /// </summary>
   4: /// <param name="values">The values to concatenate.</param>
   5: /// <returns>The concatenated sequence.</returns>
   6: public static string Concatenate(this IEnumerable<string> values)
   7: {
   8:     return values.Concatenate(string.Empty);
   9: }
  10:  
  11: /// <summary>
  12: /// Concatenates all the strings in the specified sequence, separated by
  13: /// the specified separator.
  14: /// </summary>
  15: /// <param name="values">The values to concatenate.</param>
  16: /// <param name="separator">A string that will be inserted between all the values
  17: /// in the resulting string.</param>
  18: /// <returns>The concatenated sequence, separated by
  19: /// the specified separator.</returns>
  20: public static string Concatenate(this IEnumerable<string> values, string separator)
  21: {
  22:     return values.Concatenate(separator, string.Empty, string.Empty);
  23: }
  24:  
  25: /// <summary>
  26: /// Concatenates all the strings in the specified sequence, separated by
  27: /// the specified separator, prefixed by the value specified in <paramref name="prefix" /> and
  28: /// suffixed by the value specified in <paramref name="suffix"/>.
  29: /// </summary>
  30: /// <param name="values">The values to concatenate.</param>
  31: /// <param name="separator">A string that will be inserted between all the values
  32: /// in the resulting string.</param>
  33: /// <param name="prefix">A string that will be the start of the result string.</param>
  34: /// <param name="suffix">A string that will be the end of the result string.</param>
  35: /// <returns>The concatenated sequence, separated by
  36: /// the specified separator, prefixed by the value specified in <paramref name="prefix" /> and
  37: /// suffixed by the value specified in <paramref name="suffix"/>.</returns>
  38: public static string Concatenate(this IEnumerable<string> values, string separator, string prefix, string suffix)
  39: {
  40:     Guard.ArgumentNullException(values, "values");
  41:     Guard.ArgumentNullException(separator, "separator");
  42:     Guard.ArgumentNullException(prefix, "prefix");
  43:     Guard.ArgumentNullException(suffix, "suffix");
  44:  
  45:     var result = new StringBuilder();
  46:  
  47:     result.Append(prefix);
  48:  
  49:     foreach (var value in values)
  50:     {
  51:         if (result.Length > prefix.Length)
  52:         {
  53:             result.Append(separator);
  54:         }
  55:  
  56:         result.Append(value);
  57:     }
  58:  
  59:     result.Append(suffix);
  60:  
  61:     return result.ToString();
  62: }

… and some NUnit-tests:

   1: [TestFixture]
   2: public class sequence_of_strings_Concatenate
   3: {
   4:     string[] sequence;
   5:  
   6:     [SetUp]
   7:     public void SetUp()
   8:     {
   9:         this.OnSetUp();
  10:     }
  11:  
  12:     protected virtual void OnSetUp()
  13:     {
  14:         sequence = new string[] { "a", "b", "c" };
  15:     }
  16:  
  17:     [Test, ExpectedException(typeof(ArgumentNullException))]
  18:     public void should_throw_exception_when_sequence_is_null()
  19:     {
  20:         Legend.Collections.Collection.Concatenate(null);
  21:     }
  22:  
  23:     [Test]
  24:     public void should_concatenate_values()
  25:     {
  26:         var result = sequence.Concatenate();
  27:         Assert.AreEqual(result, "abc");
  28:     }
  29: }
  30:  
  31: [TestFixture]
  32: public class sequence_of_strings_Concatenate_with_specified_separator
  33: {
  34:     string[] sequence;
  35:  
  36:     [SetUp]
  37:     public void SetUp()
  38:     {
  39:         this.OnSetUp();
  40:     }
  41:  
  42:     protected virtual void OnSetUp()
  43:     {
  44:         sequence = new string[] { "a", "b", "c" };
  45:     }
  46:  
  47:     [Test, ExpectedException(typeof(ArgumentNullException))]
  48:     public void should_throw_exception_when_sequence_is_null()
  49:     {
  50:         Legend.Collections.Collection.Concatenate(null, ",");
  51:     }
  52:  
  53:     [Test, ExpectedException(typeof(ArgumentNullException))]
  54:     public void should_throw_exception_when_separator_is_null()
  55:     {
  56:         sequence.Concatenate(null);
  57:     }
  58:  
  59:     [Test]
  60:     public void should_concatenate_values_separated_by_separator()
  61:     {
  62:         var result = sequence.Concatenate(", ");
  63:         Assert.AreEqual(result, "a, b, c");
  64:     }
  65: }
  66:  
  67: [TestFixture]
  68: public class sequence_of_strings_Concatenate_with_specified_separator_prefix_and_suffix
  69: {
  70:     string[] sequence;
  71:  
  72:     [SetUp]
  73:     public void SetUp()
  74:     {
  75:         this.OnSetUp();
  76:     }
  77:  
  78:     protected virtual void OnSetUp()
  79:     {
  80:         sequence = new string[] { "a", "b", "c" };
  81:     }
  82:  
  83:     [Test, ExpectedException(typeof(ArgumentNullException))]
  84:     public void should_throw_exception_when_sequence_is_null()
  85:     {
  86:         Legend.Collections.Collection.Concatenate(null, ",", string.Empty, string.Empty);
  87:     }
  88:  
  89:     [Test, ExpectedException(typeof(ArgumentNullException))]
  90:     public void should_throw_exception_when_separator_is_null()
  91:     {
  92:         sequence.Concatenate(null, string.Empty, string.Empty);
  93:     }
  94:  
  95:     [Test, ExpectedException(typeof(ArgumentNullException))]
  96:     public void should_throw_exception_when_prefix_is_null()
  97:     {
  98:         sequence.Concatenate(string.Empty, null, string.Empty);
  99:     }
 100:  
 101:     [Test, ExpectedException(typeof(ArgumentNullException))]
 102:     public void should_throw_exception_when_suffix_is_null()
 103:     {
 104:         sequence.Concatenate(string.Empty, string.Empty, null);
 105:     }
 106:  
 107:     [Test]
 108:     public void should_concatenate_string_with_separator_prefix_and_suffix_appended()
 109:     {
 110:         var result = sequence.Concatenate(", ", "(", ")");
 111:         Assert.AreEqual("(a, b, c)", result);
 112:     }
 113: }

Sunday, February 1, 2009

When time is of the essence

To write tests for classes with strong, hard coded, dependencies is always hard. One very common dependency is the dependency on the DateTime.Now-property. For example we have a “HelloWorld”-class (I’ve never written a Hello World-example actually so I just had to) that should greet us differently depending on the time of day.

   1: public class HelloWorld
   2: {
   3:     public string Greet()
   4:     {
   5:         var hour = DateTime.Now.Hour; 
   6:  
   7:         if (hour > 6 && hour < 12)
   8:         {
   9:             return "Good morning world!";
  10:         } 
  11:  
  12:         if (hour > 20 || hour <= 6)
  13:         {
  14:             return "Good night world!";
  15:         } 
  16:  
  17:         return "Hello world!";
  18:     }
  19: }

This code is very hard to test because we would have to either run the test on different times of the day to see that it returns the correct result or we would have to make the test set the system time in order to test the output. What we would like to do is to stub, mock or in some other way fake out the DateTime.Now property but we can’t since it’s a static. To break this dependency we’ll have to add some layer of indirection between the code and the static property. This is where dependency injection comes into play. As the code is right now, there’s no way for someone looking from the outside to see that this class has a dependency on the DateTime.Now-property. Using dependency injection and more specifically constructor injection makes this very clear.

First of all, let’s create our “layer of indirection”, the SystemTime-delegate which is a simple delegate type that returns the current time of the system.:

   1: public delegate DateTime SystemTime();

Now we make this dependency explicit by having our class take an instance of a SytemTime-delegate in it’s constructor and also we’ll use this delegate to access the system time rather than the DateTime.Now-property:

   1: public class HelloWorld
   2: {
   3:     SystemTime systemTime; 
   4:  
   5:     public HelloWorld(SystemTime systemTime)
   6:     {
   7:         this.systemTime = systemTime;    
   8:     } 
   9:  
  10:     public string Greet()
  11:     {
  12:         var hour = systemTime().Hour; 
  13:  
  14:         if (hour > 6 && hour < 12)
  15:         {
  16:             return "Good morning world!";
  17:         } 
  18:  
  19:         if (hour > 20 || hour <= 6)
  20:         {
  21:             return "Good night world!";
  22:         } 
  23:  
  24:         return "Hello world!";
  25:     }
  26: }
  27:  

Now it’s very easy to create tests that verify the output at different times by passing SystemTime-delegates that provide different times of the day:

   1: [TestFixture]
   2: public class HelloWorldTests
   3: {
   4:     [Test]
   5:     public void Greet_should_return_good_morning_in_the_morning()
   6:     {
   7:         HelloWorld greeter = new HelloWorld(() => DateTime.Parse("2000-01-01 07:00", CultureInfo.InvariantCulture));
   8:         
   9:         var greetingPhrase = greeter.Greet();
  10:  
  11:         Assert.AreEqual("Good morning world!", greetingPhrase);
  12:     }
  13:  
  14:     [Test]
  15:     public void Greet_should_return_good_night_in_the_evening()
  16:     {
  17:         HelloWorld greeter = new HelloWorld(() => DateTime.Parse("2000-01-01 21:00", CultureInfo.InvariantCulture));
  18:  
  19:         var greetingPhrase = greeter.Greet();
  20:  
  21:         Assert.AreEqual("Good night world!", greetingPhrase);
  22:     }
  23: }

And of course for production code you’d just pass in a delegate that access the DateTime.Now-property, preferably using an IoC-container, but it could certainly be done manually.

   1: var greeter = new HelloWorld(() => DateTime.Now);