DateTime DSL: Finishing Out the DSL

01.25.2012 09:03 by kbeckman | Comments

C4SC DSL Series Catalog

GitHub Repo

 

Well, Readers… I’m proud to say that the C4SC DateTime DSL is finally complete (as far as this blog series is concerned anyway). Below I detail some changes that I mentioned in my prior post as well as a few that hadn’t thought of at the time. With 170+ unit tests covering the various pieces of DSL functionality, this is finally ready for production! Check out the complete changes in their entirety in my C4SC GitHub Repo.

 

Adding the Week() and Weeks() Methods…

This enhancement was extremely easy considering that the necessary DSL plumbing was already there. Adding the Week() and Weeks() methods simply required multiplying the Day component of the DateTimeComponents instance by 7. I’ll spare you the unit test updates as they don’t look any different than before. They just account for any additional days added or subtracted by Week() or Weeks().

/// <summary>
/// Converts an Int32 to its equivalent DateTimeComponents representation in weeks. 
/// </summary>
/// <param name="weeks">Number of weeks.</param>
/// <returns><see cref="DateTimeComponents"/> composed of the given week parameter.</returns>
public static DateTimeComponents Week(this Int32 weeks)
{
    return weeks.Weeks();
}

/// <summary>
/// Converts an Int32 to its equivalent DateTimeComponents representation in weeks. 
/// </summary>
/// <param name="weeks">Number of weeks.</param>
/// <returns><see cref="DateTimeComponents"/> composed of the given weeks parameter.</returns>
public static DateTimeComponents Weeks(this Int32 weeks)
{
    return new DateTimeComponents(0, 0, weeks * 7, 0, 0, 0);
}

 

Yesterday and Tomorrow…

Implementing the DSL equivalent of Yesterday and Tomorrow is definitely the portion of the DSL that I’m least proud of. The static nature of C# required me to write way more code than I wanted to pull this off. Having a language feature like modules would have made this a trivial exercise as I would have been able to include a globally available set of methods for Yesterday and Tomorrow. Instead, I created a static class for both Yesterday and Tomorrow each with a Date() method that returns the desired DateTime value. A whole class you say? Yeah I know… It was the only way I could think of though to achieve the syntax that I wanted… I guess it’s a small price to pay for better code readability. The DateTimeNowAdapter noise you see below is explained here for those of you just joining in.

/// <summary>
/// DateTime DSL implementation for Yesterday's date.
/// </summary>
public static class Yesterday
{
    private static IDateTimeNowAdapter _nowAdapter = new SystemDateTimeNowAdapter();
    private static readonly object _syncLock = new object();

    /// <summary>
    /// Sets the DSL's IDateTimeNowAdapter. This is the injection point for tests to provide their own adapter
    /// implementation.
    /// </summary>
    /// <param name="adapter"><see cref="IDateTimeNowAdapter"/> implementation.</param>
    internal static void SetDateTimeNowAdapter(IDateTimeNowAdapter adapter)
    {
        lock (_syncLock) { _nowAdapter = adapter; }
    }

    /// <summary>
    /// Yesterday's Date.
    /// </summary>
    /// <returns>Yesterday's date value 24 hours ago.</returns>
    public static DateTime Date()
    {
        return _nowAdapter.DateTimeNow().Date.AddDays(-1);
    }
}

 

Saving the Best For Last… The [Almost] Complete .NET Port of the Rails Date Class

Throughout this series, I’ve commented that the inspiration for this DSL was syntax I encountered in the Ruby language – more specifically the helper methods in the Rails Date class. All of the work up to this point for calculating DateTime covered only a portion of the methods and functionality available in Rails’ Date class. But in the spirit of open source, I wanted to finish this DSL out for anyone besides me who might want to use it.  Instead of providing a set of methods that act upon a semantic model to cache a set of calculations I wanted to make on a DateTime value, I was simply able to add some extension methods to the native .NET DateTime Type. For brevity, only a partial class implementation is below.

 

Anyone who is familiar with the Rails Date class will notice some missing methods… Luckily for me, the functionality in the missing methods is available as part of the native .NET DateTime or String Types and didn’t need to be accounted for here. These are things like to_datetime (DateTime.Parse()), to_formatted_s (String.Format), acts_like_date?, etc. There are also a lot of method aliases that I left out in favor of a single method syntax for a given piece of functionality. For my .NET readers, Ruby aliases are just another name for method delegation where you’d wrap an existing method call with another method with a better or different name.

/// <summary>
/// DateTime extension methods. This class is a .NET partial port of the helper methods in the Rails Date class.
/// </summary>
/// <remarks>
/// http://api.rubyonrails.org/classes/Date.html
/// </remarks>
public static class DateTimeExtensions
{
    private static readonly int[] _q1 = new[] { 1, 2, 3 };
    private static readonly int[] _q2 = new[] { 4, 5, 6 };
    private static readonly int[] _q3 = new[] { 7, 8, 9 };

    /// <summary>
    ///    Resets the time portion of the DateTime to 00:00:00.
    /// </summary>
    /// <param name="dateTime">DateTime evaluation target.</param>
    /// <returns>Given date value at 00:00:00.</returns>
    public static DateTime AtMidnight(this DateTime dateTime)
    {
        return dateTime.AtBeginningOfDay();
    }

    /// <summary>
    ///    Resets the time portion of the DateTime to 00:00:00.
    /// </summary>
    /// <param name="dateTime">DateTime evaluation target.</param>
    /// <returns>Given date value at 00:00:00.</returns>
    public static DateTime AtBeginningOfDay(this DateTime dateTime)
    {
        return dateTime.Date;
    }

    /// <summary>
    ///    Resets the day value to Sunday of the current week at 00:00:00.
    /// </summary>
    /// <param name="dateTime">DateTime evaluation target.</param>
    /// <returns>Sunday of the current week at 00:00:00.</returns>
    public static DateTime AtBeginningOfWeek(this DateTime dateTime)
    {
        return dateTime.AddDays(-(Int32)dateTime.DayOfWeek).AtBeginningOfDay();
    }

    /// <summary>
    ///    Resets the day value to the first of the month at 00:00:00.
    /// </summary>
    /// <param name="dateTime">DateTime evaluation target.</param>
        /// <returns>First day of the month at 00:00:00.</returns>
    public static DateTime AtBeginningOfMonth(this DateTime dateTime)
    {
        return new DateTime(dateTime.Year, dateTime.Month, 1);
    }

    /// <summary>
    ///    Resets the date value to the first day of the quarter at 00:00:00.
    /// </summary>
    /// <param name="dateTime">DateTime evaluation target.</param>
    /// <returns>First day of the quarter at 00:00:00.</returns>
    public static DateTime AtBeginningOfQuarter(this DateTime dateTime)
    {
        return _q1.Contains(dateTime.Month) ? new DateTime(dateTime.Year, 1, 1).AtBeginningOfDay() :
            _q2.Contains(dateTime.Month) ? new DateTime(dateTime.Year, 3, 1).AtBeginningOfDay() :
            _q3.Contains(dateTime.Month) ? new DateTime(dateTime.Year, 7, 1).AtBeginningOfDay() :
                new DateTime(dateTime.Year, 10, 1).AtBeginningOfDay();
    }

    /// <summary>
    ///    Resets the day value to January 1 of the current year at 00:00:00.
    /// </summary>
    /// <param name="dateTime">DateTime evaluation target.</param>
    /// <returns>January 1 of the current year at 00:00:00.</returns>
    public static DateTime AtBeginningOfYear(this DateTime dateTime)
    {
        return new DateTime(dateTime.Year, 1, 1);
    }
}

 

A Quick Moment To Reflect…

This series was a LOT of fun and incredibly educational for me. It allowed me to take my new appreciation for more English-readable code from my recent work with Ruby and open source projects and apply it to a static language in C#. I’ll be taking these new techniques forward with me in future .NET development and I won’t be one to settle with the syntax offered by the .NET Framework or other frameworks. I hope you enjoyed this as much as I did!

 

Comments welcome!

DateTime DSL: Method Chaining

01.08.2012 08:54 by kbeckman | Comments

C4SC DSL Series Catalog

GitHub Repo

 

Continuing on with the DateTime DSL development… It’s time to add some method chaining DateTimeComponents to make the DSL even more robust.

 

Looking Ahead to the Desired DSL Syntax…

Until now, if you’ve been following along the with this series, the DateTime DSL syntax has been pretty clean. And by “pretty clean” I mean, that the parentheses usage has been kept at a minimum. Now that I’ll be implementing chaining on the DateTimeComponents struct, there’s going to be a few more parentheses than I’d prefer. But hey, we’re not using Ruby… In Ruby, parentheses are really only required when you need them for better readability. As you’ll see below, the chaining introduces consecutive parentheses because of the argument required by the And() method. I wish there was a way to avoid it here, but sometimes you just have to accept the limitations of the language you’re using…

DateTime lunchTime     = 3.Hours().And(45.Minutes()).FromNow();
DateTime endOfWorld    = 11.Months().And(21.Days()).From(newYearsEve2011);
DateTime openingBell   = 6.Hours().And(30.Minutes()).AgoFrom(closingBell);
DateTime raceStart     = 3.Minutes().And(42.Seconds()).Ago();

 

The And() Method

As far as the DSL is concerned, we get an incredible amount of additional functionality from And(). With the addition of this single extension method to DateTimeComponents, the DSL now offers the functionality to modify all elements of DateTime before the conversion to the DateTime Type. It accomplishes this by returning a new instance of DateTimeComponents where all properties are summed from the participating DateTimeComponents operands. The And() method gives you unlimited chaining on DateTimeComponents (unless there’s some language/compiler limitation that I’m not aware of in C#) allowing for essentially any combination. However, for readability’s sake, I would suggest a limit of two or three. The code will simply read better and it will sound a lot more like proper, spoken English. You’ll see from the unit tests below that more than two or three usages of And() gets a little wordy.

/// <summary>
/// Extension methods to support method-chaining on DateTimeComponents allowing for modification of all components
/// of date and time before the actual conversion to <see cref="DateTime"/>.
/// </summary>
public static class DateTimeComponentsChaining
{
    public static DateTimeComponents And(this DateTimeComponents components, DateTimeComponents operand)
    {
        return new DateTimeComponents(components.Years + operand.Years
                                    , components.Months + operand.Months
                                    , components.Days + operand.Days
                                    , components.Minutes + operand.Minutes
                                    , components.Seconds + operand.Seconds
                                    , components.Milliseconds + operand.Milliseconds);
    }
}

 

The Unit Tests

And voila! Chaining! For brevity, I’ve only included the addition tests that calculate the DateTime FromNow(). The addition tests that calculate DateTime from a given DateTime (using the From() method) as well as all of the relevant subtraction tests (using the Ago() and AgoFrom() methods) look almost exactly the same. For those of you just joining in who aren’t aware of the _nowAdapter usage below, please refer to the last post on stubbing static dependencies.

[Test]
public void it_should_add_1_of_each_component_to_now()
{
    DateTime actual = 1.Year()
                       .And(1.Month())
                       .And(1.Day())
                       .And(1.Minute())
                       .And(1.Second())
                       .And(1.Millisecond()).FromNow();

    DateTime expected = _nowAdapter.DateTimeNow().AddYears(1)
                            .AddMonths(1)
                            .AddDays(1)
                            .AddMinutes(1)
                            .AddSeconds(1)
                            .AddMilliseconds(1);

    Assert.That(actual, Is.EqualTo(expected));
}

[Test]
[TestCase(1, 2, 3, 4, 5, 6)]
[TestCase(2, 4, 6, 8, 10, 12)]
[TestCase(5, 10, 15, 20, 25, 30)]
[TestCase(100, 200, 300, 400, 500, 600)]
public void it_should_add_all_components_to_now(Int32 years, Int32 months, Int32 days, Int32 minutes, Int32 seconds, Int32 milliseconds)
{
    DateTime actual = years.Years()
                        .And(months.Months())
                        .And(days.Days())
                        .And(minutes.Minutes())
                        .And(seconds.Seconds())
                        .And(milliseconds.Milliseconds()).FromNow();

    DateTime expected = _nowAdapter.DateTimeNow().AddYears(years)
                            .AddMonths(months)
                            .AddDays(days)
                            .AddMinutes(minutes)
                            .AddSeconds(seconds)
                            .AddMilliseconds(milliseconds);

    Assert.That(actual, Is.EqualTo(expected));
}

 

Wrap-Up

We’re nearing the end of this C4SC series, but there is at least a couple more things I’d like to address. I’m not sure if I’ll have one or two more posts, but here’s a sneak peak at some of the things I’ll be addressing…

  • I think the DSL needs some helper methods for common DateTime-related spoken English. In other words, I want a few helpers like Yesterday(),Tomorrow(), etc.; and maybe an addition to the Int32ConversionToDateTimeComponents extension methods for Week(); and possibly a few others… I’ve really been thinking about this a lot over the last few days. With a language feature like modules (i.e. Ruby and VB.NET), I could easily pull off the Yesterday() and Tomorrow() methods. However, since this is all in C#, they’re probably going to be static methods on a static class. Not a big deal at all functionality-wise, but it’s going to take some thought (and maybe a trip or two to the thesaurus) to make the DSL syntax accommodate this.
  • I keep thinking there’s a better way to handle the unit test organization, but I haven’t come up with it yet. Between the two current unit test fixtures, every combination of the DSL syntax is covered, but I might break it out into some more fixtures to get rid of the #region blocks partitioning the tests.

See you next time…

DateTime DSL: Promoting Testability by Stubbing Static Dependencies

12.16.2011 08:35 by kbeckman | Comments

C4SC DSL Series Catalog

GitHub Repo

 

In the last post, I closed by describing the timing problem when the DSL’s unit tests evaluated expected values calculated DateTime.Now. When the unit tests regained the execution focus back from the DSL after the DateTime calculation, the value of DateTime.Now had incremented slightly by a few milliseconds throwing off the expected results. We’ll be taking a hand-rolled approach to solving the issue here…

 

Overview

Your static class has a static dependency that is either very difficult or impossible to test around. The dependency might be a base type in the .NET Framework or it could be a static type that you may or may not own.

 

Instead of calling the static dependency directly, use the Adapter Pattern to wrap the static dependency call in an instance method of a class implementing a common public interface. Provide an injection point in your static class where you can provide a stub if necessary for use in unit testing.

 

Motivation

I arrived a hand-rolled solution for resolving the static DateTime.Now dependency because it seemed to fit the situation the best. I could have solved this a couple of other ways, but I’ll explain why I think the hand-rolled approach is better in this case. This issue could have easily been solved by using a mocking or isolation framework that provides static mocking or .NET Framework base type mocking. My favorite tool for that job is Telerik’s JustMock, but like I said in the last post, the free version doesn’t support static mocking and I don’t want to add a $299 dependency for the professional version to my open source DSL.

 

Mechanics (Examples Using Actual DSL Implementation)

1) Create a public interface that defines the functionality provided by the static dependency.

/// <summary>
/// Defines a service for providing the DateTime.Now value. This can be used in testing scenarios to provide a 
/// testable DateTime.Now value configurable from unit test setup methods.
/// </summary>
public interface IDateTimeNowAdapter
{
    DateTime DateTimeNow();
}

 

2) Implement the public interface and delegate all methods defined in the interface to the static dependency.

/// <summary>
/// IDateTimeNowAdapter implementation that provides the system's actual DateTime.Now value.
/// </summary>
public class SystemDateTimeNowAdapter : IDateTimeNowAdapter
{
    public DateTime DateTimeNow()
    {
        return DateTime.Now;
    }
}

 

3) Provide an alternate implementation to the public interface that allows for testability. This particular implementation takes any instance of DateTime as a constructor parameter argument. When this Type is instantiated in the unit tests, the call to the DateTimeNow() method will always provide the DateTime instance provided at instantiation and allow the test methods access to the exact DateTime value used for the DateTimeNow() calls within the test target class.

/// <summary>
/// IDateTimeNowAdapter implementation that provides configurable DateTime.Now value for use in unit testing.
/// </summary>
public class TestDateTimeNowAdapter : IDateTimeNowAdapter
{
    private readonly DateTime _dateTimeNowValue;

    public TestDateTimeNowAdapter(DateTime dateTimeNowValue)
    {
        _dateTimeNowValue = dateTimeNowValue;
    }

    public DateTime DateTimeNow()
    {
        return _dateTimeNowValue;
    }
}

 

4) Modify the test target class to use an instance of the Type constructed in Step 2. Be sure to use the public interface definition rather than a hard reference to the instance Type. Delegate all calls initially made to the static dependency to the public interface of the instance Type. This will break the immediate dependency your test target class has on the static dependency allowing you to provide an alternate implementation when needed for the tests.

Before…

/// <summary>
/// Calculates the result of DateTimeComponents added to DateTime.Now().
/// </summary>
/// <param name="components"><see cref="DateTimeComponents"/> operand.</param>
/// <returns>Future DateTime calculated from DateTime.Now().</returns>
public static DateTime FromNow(this DateTimeComponents components)
{
    return components.From(DateTime.Now);
}

/// <summary>
/// Calculates the result of DateTimeComponents subtracted from DateTime.Now().
/// </summary>
/// <param name="components"><see cref="DateTimeComponents"/> operand.</param>
/// <returns>Past DateTime calculated from DateTime.Now().</returns>
public static DateTime Ago(this DateTimeComponents components)
{
    return components.AgoFrom(DateTime.Now);
}

After…

/// <summary>
/// Extension methods to calculate DateTime future or past result from DateTimeComponents operand.
/// </summary>
public static class DateTimeComponentsConversionToDateTime
{
    private static IDateTimeNowAdapter _nowAdapter = new SystemDateTimeNowAdapter();

    //Additional class implementation details omitted for this example...

    /// <summary>
    /// Calculates the result of DateTimeComponents added to DateTime.Now().
    /// </summary>
    /// <param name="components"><see cref="DateTimeComponents"/> operand.</param>
    /// <returns>Future DateTime calculated from DateTime.Now().</returns>
    public static DateTime FromNow(this DateTimeComponents components)
    {
        return components.From(_nowAdapter.DateTimeNow());
    }

    /// <summary>
    /// Calculates the result of DateTimeComponents subtracted from DateTime.Now().
    /// </summary>
    /// <param name="components"><see cref="DateTimeComponents"/> operand.</param>
    /// <returns>Past DateTime calculated from DateTime.Now().</returns>
    public static DateTime Ago(this DateTimeComponents components)
    {
        return components.AgoFrom(_nowAdapter.DateTimeNow());
    }    
}

 

4) Provide an internally scoped injection point in your test target class allowing you to replace the default public interface implementation with the Type defined in Step 3. A couple of things here… I’m providing an internally scoped method in this example because I don’t want anything publicly available that consumers of this DSL might be able to use to alter the expected behavior. This injection point is something I want available only to unit tests (described in Step 5). You’ll notice that I’m using the C# lock keyword here to protect the dependency injection point. Since this is a static class and the IDateTimeNowProvider is a static instance, synchronization must be provided to ensure other threads aren’t accessing the same resource we’re trying to swap out here.

 

A seasoned developer might ask why I haven’t just implemented a IoC container to provide the configurable dependency injection here. The short answer is that I felt it would be overkill to implement it for an adapter pattern that simply wraps a call to DateTime.Now. An IoC container would provide a lot of overhead for very little impact here. Additionally, I believe that when you are building frameworks or reusable components, there is no reason to introduce additional dependencies like an IoC container. I would prefer that someone could drop in this DSL in as-is without having to include any other DLL dependencies. IoC containers are better left for actual application code with more involved dependencies.

private static readonly object _syncLock = new object();
        
/// <summary>
/// Sets the DSL's IDateTimeNowAdapter. This is the injection point for tests to provide their own adapter
/// implementation.
/// </summary>
/// <param name="adapter"><see cref="IDateTimeNowAdapter"/> implementation.</param>
internal static void SetDateTimeNowAdapter(IDateTimeNowAdapter adapter)
{
    lock (_syncLock) { _nowAdapter = adapter; }
}
 

5) Set the InternalsVisibleTo attribute in the assembly containing your test target class to allow your unit test assembly access the internal methods of its Types. This will allow the unit test assembly to access the test target class’s internal methods just as if they were of public scope.

//AssemblyInfo.cs for the assembly containing the test target class..

[assembly: InternalsVisibleTo("Test.C4SC")]

 

6) Use the testable adapter class in your unit tests to replace the default adapter implementation. Notice here that the test class has a private instance IDateTimeNowAdapter field. This allows for DRY tests where the test target’s adapter can be set using NUnit’s SetUp and TearDown methods rather than in each individual test.

private IDateTimeNowAdapter _nowAdapter;

/// <summary>
/// Executes individual Test-level setup and initialization prior to each test being run.
/// </summary>
[SetUp]
public void SetUpTest()
{
    //Replace the test target's default IDateTimeNowAdapter...
    nowAdapter = new TestDateTimeNowAdapter(DateTime.Now);
    DateTimeComponentsConversionToDateTime.SetDateTimeNowAdapter(_nowAdapter);
}


/// <summary>
/// Executes individual Test-level clean-up after each test has been run. This method is guaranteed to run 
/// even if an exception is thrown.
/// </summary>
[TearDown]
public void TearDownTest()
{
    //Reset the test target's IDateTimeNowAdapter to the system adapter...
    DateTimeComponentsConversionToDateTime.SetDateTimeNowAdapter(new SystemDateTimeNowAdapter());
}

[Test]
public void it_should_add_1_month_to_now()
{
    DateTime actual      = 1.Month().FromNow();
    DateTime expected    = _nowAdapter.DateTimeNow().AddMonths(1);

    Assert.That(actual, Is.EqualTo(expected));
}

DateTime DSL: Refactoring and DRYing Up Unit Tests

12.11.2011 13:41 by kbeckman | Comments

C4SC DSL Series Catalog

GitHub Repo

 

A few weeks ago, I started a C4SC series walking through the development of a DateTime DSL in C#. Before moving forward with building out additional functionality, I need to address some issues with the unit tests. There are a couple issues in particular that need attention: a) there’s a lot of duplicated effort in the unit test assertion code, and b) there’s a timing issue using DateTime.Now in the DSL’s Ago() (subtraction) and FromNow() (addition) methods. I’ll explain further as each are addressed…

 

DRYing Up Assertion Code

All of the unit tests so far are nearly identical. Aside from the individual DateTime calculation that’s being tested, all assertions are exactly the same. In addition, there are six separate assertions testing each individual component of DateTime rather than evaluating the DateTime result in its entirety. I used this technique early on in the DSL’s development so I could evaluate the individual components of DateTime to help me more quickly target bugs in the development process. However, now I’m confident in the DSL’s evolution and there’s no longer a need for this level of fine-grained assertion. An overall assertion of the DateTime value as a whole will suffice from now on.

Before…

[Test]
public void it_should_add_1_year_to_now()
{
    DateTime actual      = 1.Year().FromNow();
    DateTime expected    = DateTime.Now.AddYears(1);
 
    Assert.That(actual.Year, Is.EqualTo(expected.Year));
    Assert.That(actual.Month, Is.EqualTo(expected.Month));
    Assert.That(actual.Day, Is.EqualTo(expected.Day));
    Assert.That(actual.Minute, Is.EqualTo(expected.Minute));
    Assert.That(actual.Second, Is.EqualTo(expected.Second));
    Assert.That(actual.Millisecond, Is.EqualTo(expected.Millisecond));
}
 
[Test]
public void it_should_subtract_1_year_from_now()
{
    DateTime actual     = 1.Year().Ago();
    DateTime expected   = DateTime.Now.AddYears(-1);
 
    Assert.That(actual.Year, Is.EqualTo(expected.Year));
    Assert.That(actual.Month, Is.EqualTo(expected.Month));
    Assert.That(actual.Day, Is.EqualTo(expected.Day));
    Assert.That(actual.Minute, Is.EqualTo(expected.Minute));
    Assert.That(actual.Second, Is.EqualTo(expected.Second));
    Assert.That(actual.Millisecond, Is.EqualTo(expected.Millisecond));
}

After…

[Test]
public void it_should_add_1_year_to_now()
{
    DateTime actual    = 1.Year().FromNow();
    DateTime expected  = DateTime.Now.AddYears(1);

    Assert.That(actual, Is.EqualTo(expected));
}

[Test]
public void it_should_subtract_1_year_from_now()
{
    DateTime actual    = 1.Year().Ago();
    DateTime expected  = DateTime.Now.AddYears(-1);

    Assert.That(actual, Is.EqualTo(expected));
}

 

Assertion Discrepancies with DateTime.Now()

The Problem…

I found early on in the testing that there was an issue asserting the expected values for the milliseconds component of time. When working with milliseconds, the expected value calculated in the tests was most times higher than expected for both the addition and subtraction operations (by at least a few milliseconds). Occasionally, the calculated expected value was actually the value I expected.

 

So why the discrepancy? The short answer is that the DSL uses DateTime.Now as the base date for calculations in the FromNow() and Ago() methods.

/// <summary>
/// Calculates the result of DateTimeComponents added to DateTime.Now().
/// </summary>
/// <param name="components"><see cref="DateTimeComponents"/> operand.</param>
/// <returns>Future DateTime calculated from DateTime.Now().</returns>
public static DateTime FromNow(this DateTimeComponents components)
{
    return components.From(DateTime.Now);
}

 

The DateTime.Now value is also used to calculate the expected value in the tests.

[Test]
public void it_should_add_1_millisecond_to_now()
{
    DateTime actual    = 1.Millisecond().FromNow();
    DateTime expected  = DateTime.Now.AddMilliseconds(1);

    Assert.That(actual, Is.EqualTo(expected));
}

 

When the unit test assertions were finally invoked after the test suite got the execution control back, the DateTime.Now value had increased by a few milliseconds causing the discrepancy. I hadn’t accounted for the lag in processing time in the tests…

 

An Elegant Solution…

In Ruby, this is an easy problem to fix. The TimeCop gem is a Ruby library that allows for the mocking of the language’s DateTime.Now method. It intercepts calls to DateTime.Now and use the DateTime value that’s defined in the test instead of the value provided by the system. There are elegant solutions in .NET as well that provide more-or-less the same type of functionality. However, they’re going to cost you $$$. My preferred .NET mocking framework is Telerik’s JustMock. It has the cleanest mocking syntax that I’ve used and the professional version provides features such as static mocking and .NET base library mocking (i.e. DateTime). Unfortunately, the free version of JustMock doesn’t provide the static mocking functionality needed here and it’s getting close to Christmas so I don’t have $299 USD to pay for the professional version. More importantly, I don’t want to add a $299 dependency to anyone who wishes to use my OPEN SOURCE DSL. We’ll have to figure out something else…

 

And for that, we’ll need another post… Stay Tuned.

Foundation of a .NET DateTime DSL

11.17.2011 13:57 by kbeckman | Comments

C4SC DSL Series Catalog

 

Welcome to the first of several posts in the development of a DSL for working with DateTime in .NET. I’ll keep the intro light as there are a lot of things to cover. I just wanted to mention one thing though before we get started… Some of the class names you’ll see below are extremely wordy, but by the name alone they convey the full intent of the class internals. The wordy class names are only for the static methods containing the DSL’s extension methods. We’ll never have a need to instantiate these classes so you won’t see them in any code that uses it.

 

Looking Ahead to the Desired DSL Syntax…

The following code snippets are before-and-after examples of how to calculate some sample dates using the traditional approach available in the .NET Framework followed by the desired syntax I hope to achieve using the DateTime DSL we’re beginning in this post. You’ll notice here that the DSL doesn’t exactly save you many keystrokes, but it adds a great deal to the code’s readability and immediate understandability. I can see immediate usages for this DSL in providing a more readable unit test syntax for calculating and evaluating DateTime objects. I can also think of several times I wish I had something like this for manipulating DateTime in domain objects.

//Traditional .NET Framework...
DateTime tomorrow            = DateTime.Today.AddDays(1);
DateTime yesterday           = DateTime.Today.Subtract(new TimeSpan(-1));
DateTime oneYearFromNow      = DateTime.Today.AddYears(1);
DateTime silverAnniversary   = new DateTime(2012, 10, 20).AddYears(25);

 

//DSL Syntax...
DateTime tommorow             = 1.Day().FromNow();
DateTime yesterday            = 1.Day().Ago();
DateTime oneYearFromNow       = 1.Year().FromNow();
DateTime silverAnniversary    = 25.Years().From(user.weddingDate);

 

Step 1: The DSL Semantic Model

Martin Fowler defines a semantic model as “The model that’s populated by a DSL.” For the purposes of this post, that doesn’t tell us much… Without asking you to buy the book for further reading, I’ll provide a little more detail before we get to the code. In version 3.5 of the .NET Framework, Microsoft introduced extension methods allowing developers to extend class behavior by providing an additional set of methods (via a static class) without having to extend the original class implementation. This was an extremely power feature because it allowed .NET developers to extend classes in closed libraries or purchased 3rd party libraries where no source code was available. This approach is a similar technique to using modules in Ruby mixins, but it’s more restrictive since extension methods are constrained to a single Type. Adding extension methods for base classes and interface Types allows for more flexibility, but it’s still not as forgiving as what a truly dynamic language can provide.

 

In DSLs (a term used lightly here) that only use extension methods, the semantic model is whatever Type the extension methods are targeted for. In the case of this DSL as seen in the examples above, the desired syntax uses extension methods defined for the Int32 Type and not the final result Type of DateTime. Because a conversion between the two is required, we need an intermediate semantic model that can aggregate the components of DateTime before the actual DateTime instance is created. For that purpose, I’ve created an immutable struct called DateTimeComponents that contains a read-only field for all of the components of DateTime in the .NET Framework: years, months, days, minutes, seconds and milliseconds.

/// <summary>
/// Immutable semantic model for C4SC DateTime DSL.
/// </summary>
/// <remarks>
/// http://martinfowler.com/dslCatalog/semanticModel.html
/// </remarks>
public struct DateTimeComponents
{
    public DateTimeComponents(Int32 years, Int32 months, Int32 days, 
        Int32 minutes, Int32 seconds, Int32 milliseconds)
    {
        _years        = years;
        _months       = months;
        _days         = days;
        _minutes      = minutes;
        _seconds      = seconds;
        _milliseconds = milliseconds;
    }

    private readonly Int32 _years;
    public Int32 Years { get { return _years; } }

    private readonly Int32 _months;
    public Int32 Months { get { return _months; } }

    private readonly Int32 _days;
    public Int32 Days { get { return _days; } }

    private readonly Int32 _minutes;
    public Int32 Minutes { get { return _minutes; } }

    private readonly Int32 _seconds;
    public Int32 Seconds { get { return _seconds; } }

    private readonly Int32 _milliseconds;
    public Int32 Milliseconds { get { return _milliseconds; } }
}

 

Step 2: The Int32 Extension Methods

The DateTimeComponents struct is our semantic model that essentially collects the components of DateTime we will eventually convert to a DateTime object. As you’ll see from the abbreviated Int32ConversionToDateTimeComponents static class below, the extension methods here are extremely trivial in their functionality. They simply call the DateTimeComponents constructor passing in the various component of DateTime into the necessary constructor parameter and return the DateTimeComponents instance as the result. You’ll also notice that the singular version of each component method simply delegates control to its plural counter part. The abbreviated example below only shows the extension methods for collecting the year component of DateTime (although all other components are implemented). To see the full class implementation, visit my C4SC GitHub repository.

/// <summary>
/// Extension methods to convert Int32 values to their DateTimeComponents equivalent.
/// </summary>
public static class Int32ConversionToDateTimeComponents
{
    /// <summary>
    /// Converts an Int32 to its equivalent DateTimeComponents representation in years. 
    /// </summary>
    /// <param name="year">Number of years.</param>
    /// <returns><see cref="DateTimeComponents"/> composed of the given year parameter.</returns>
    public static DateTimeComponents Year(this Int32 year)
    {
        return year.Years();
    }

    /// <summary>
    /// Converts an Int32 to its equivalent DateTimeComponents representation in years. 
    /// </summary>
    /// <param name="years">Number of years.</param>
    /// <returns><see cref="DateTimeComponents"/> composed of the given years parameter.</returns>
    public static DateTimeComponents Years(this Int32 years)
    {
        return new DateTimeComponents(years, 0, 0, 0, 0, 0);
    }
}

 

Step 3: The DateTimeComponents Extension Methods

Below is the complete implementation of the DateTimeComponentsConversionToDateTime static class that contains all of the extension methods to return our desired DateTime. As you can see, these methods simply use the individual components of DateTime and either add or subtract them from the given DateTime. There are some slight optimizations we can perform here, but I’m going to save those for a future post. As I’ve mentioned before, C4SC is an agile shop and we’ll worry about refactoring and optimizing when there is sufficient unit test coverage to allow for it. If this class doesn’t make much sense yet, it will in the next section when I include some unit tests that reveal the DSL’s full intent.

/// <summary>
/// Extension methods to calculate DateTime future or past result from DateTimeComponents operand.
/// </summary>
public static class DateTimeComponentsConversionToDateTime
{
    /// <summary>
    /// Calculates the result of DateTimeComponents added to DateTime.Now().
    /// </summary>
    /// <param name="components"><see cref="DateTimeComponents"/> operand.</param>
    /// <returns>Future DateTime calculated from DateTime.Now().</returns>
    public static DateTime FromNow(this DateTimeComponents components)
    {
        return components.From(DateTime.Now);
    }

    /// <summary>
    /// Calculates the result of DateTimeComponents added to a given DateTime.
    /// </summary>
    /// <param name="components"><see cref="DateTimeComponents"/> operand.</param>
    /// <param name="dateTime">DateTime operand from which to calculate a future dateTime.</param>
    /// <returns>Future DateTime calculated from the given DateTimeComponents.</returns>
    public static DateTime From(this DateTimeComponents components, DateTime dateTime)
    {
        return dateTime.AddYears(components.Years)
            .AddMonths(components.Months)
            .AddDays(components.Days)
            .AddMinutes(components.Minutes)
            .AddSeconds(components.Seconds)
            .AddMilliseconds(components.Milliseconds);
    }

    /// <summary>
    /// Calculates the result of DateTimeComponents subtracted from DateTime.Now().
    /// </summary>
    /// <param name="components"><see cref="DateTimeComponents"/> operand.</param>
    /// <returns>Past DateTime calculated from DateTime.Now().</returns>
    public static DateTime Ago(this DateTimeComponents components)
    {
        return components.Ago(DateTime.Now);
    }

    /// <summary>
    /// Calculates the result of DateTimeComponents subtracted from a given DateTime.
    /// </summary>
    /// <param name="components"><see cref="DateTimeComponents"/> operand.</param>
    /// <param name="dateTime">DateTime operand from which to calculate a past date.</param>
    /// <returns>Past DateTime calculated from the given DateTimeComponents.</returns>
    public static DateTime Ago(this DateTimeComponents components, DateTime dateTime)
    {
        return dateTime.AddYears(-components.Years)
            .AddMonths(-components.Months)
            .AddDays(-components.Days)
            .AddMinutes(-components.Minutes)
            .AddSeconds(-components.Seconds)
            .AddMilliseconds(-components.Milliseconds);
    }
}

 

The Unit Tests

I’ve only included a couple of unit tests so you can get an idea of what we’ll actually be testing and how we’ll be testing it. I hope you’ll notice some immediate refactorings required by the tests below… We have a lot of duplicated test code across the tests and there are actually some fundamental issues with testing the results when working with the DateTime.Now value that cause the tests to fail intermittently. The intermittent failures occur mostly when testing the changes in milliseconds since those units of time are so small. For now though, they’re okay as we’ll be doing some refactoring work in the next post.

[Test]
public void it_should_add_1_year_to_now()
{
    DateTime actual      = 1.Year().FromNow();
    DateTime expected    = DateTime.Now.AddYears(1);

    Assert.That(actual.Year, Is.EqualTo(expected.Year));
    Assert.That(actual.Month, Is.EqualTo(expected.Month));
    Assert.That(actual.Day, Is.EqualTo(expected.Day));
    Assert.That(actual.Minute, Is.EqualTo(expected.Minute));
    Assert.That(actual.Second, Is.AtLeast(expected.Second));
    Assert.That(actual.Millisecond, Is.AtLeast(expected.Millisecond));
}

[Test]
public void it_should_subtract_1_year_from_now()
{
    DateTime actual     = 1.Year().Ago();
    DateTime expected   = DateTime.Now.AddYears(-1);

    Assert.That(actual.Year, Is.EqualTo(expected.Year));
    Assert.That(actual.Month, Is.EqualTo(expected.Month));
    Assert.That(actual.Day, Is.EqualTo(expected.Day));
    Assert.That(actual.Minute, Is.EqualTo(expected.Minute));
    Assert.That(actual.Second, Is.AtLeast(expected.Second));
    Assert.That(actual.Millisecond, Is.AtLeast(expected.Millisecond));
}

 

Resources