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));
}