var blog = new ThoughtStream(me); RSS 2.0
 Monday, September 29, 2008

At some point, you will need to put a timer into your C# code. Hopefully you're doing Test Driven Development, or at least unit testing, to cover your need for a timer with a unit test. If you are - I'm betting you ran into some really ugly problems like tests that would not pass without having 'Thread.Sleep(1000)' calls in them. Worse yet - last week, I put a 3 second timer into a class that was being unit tested. This one mistake cause my CruiseControl build to go from 2 minutes, to timing out at 90 minutes because NUnit was keeping the app domain alive while the timer was alive, so it never closed the test run. Oops.

To fix this problem, we decided we needed to eliminate the use of an actual timer and use an abstraction that could be mocked/controlled in our unit tests. Unfortunately, the .NET Framework does not have an actual abstraction for a timer. So, if you need to use a timer in your code - and at some point in time, you will - you'll want to create a very simple ITimer interface that would let you control when the timer elapses and fires its registered action.

Here's the core of the abstraction that we came up with, initially.

public interface ITimer
{
    void Start(Action timerAction);
}

With such a simple interface in place, we can now mock it out in our unit tests (I like Rhino Mocks) and set up an expectation on the Start method to capture the timerAction parameter, then simulate the timer elapsing by calling the timer action from our unit test. Here's an example test and 'system under test' to illustrate how we use this interface.

[Test]
public void DemonstratingHowToUnitTestATimerElapsing()
{
    Action timerElaspedAction;
    ITimer timer = MockRepository.GenerateMock<ITimer>();
    timer.Expect(t => t.Start(null)).IgnoreArguments().Callback(
    delegate(Action timerAction)
    {
        timerElapsedAction = timerAction;
        return true;
    });
 
    MySystemUnderTest sut = new MySystemUnderTest();
    sut.StartMonitoringStuff(timer);
 
    //here's where we simulate the timer elapsing
    timerElapsedAction();
 
    Assert.IsTrue(sut.TheTimerActionWasCalled);
}
 
public class MySystemUnderTest()
{
    public bool TheTimerActionWasCalled { get; set; }
 
    public MySystemUnderTest()
    {
        TheTimerActionWasCalled = false;
    }
 
    public StartMonitoringStuff(ITimer timer)
    {
        timer.Start(() => TheTimerActionWasCalled = true);
    }
}
 

There is a bit more detail to the actual ITimer interface, and the implementation, though. We ran into issues where we needed to stop the timer, prevent it from firing after we close the SUT, etc. So, all said and done, here's the full ITimer interface and MyTimer implementation that we ended up with.

public interface ITimer
{
   void Start(Action action);
   void Stop();
}
 
public class MyTimer : ITimer, IDisposable
{
 
   private TimeSpan _timerInterval;
   private Timer _timer;
 
   private Action _timerAction;
 
   private bool IsRunning { get; set; }
 
   public MyTimer(TimeSpan timerInterval)
   {
       _timerInterval = timerInterval;
   }
 
   public void Dispose()
   {
       StopTimer();
   }
 
   public void Start(Action action)
   {
       _timerAction = action;
       IsRunning = true;
       StartTimer();
   }
 
   public void Stop()
   {
       IsRunning = false;
       StopTimer();
   }
 
   private void StartTimer()
   {
       _timer = new Timer(o => Timer_Execute(), null, 0, Convert.ToInt32(_timerInterval.TotalMilliseconds));
   }
 
   private void StopTimer()
   {
       if (_timer != null)
       {
           _timer.Change(Timeout.Infinite, Timeout.Infinite);
           _timer.Dispose();
           _timer = null;
       }
   }
 
   private void Timer_Execute()
   {
       try
       {
           StopTimer();
           _timerAction();
       }
       finally
       {
           if (IsRunning)
               StartTimer();
       }
   }
 
}

This has worked out very well for us, so far. It lets us put a timer in place when we need one, but not worry about thread racing issues, long running tests, etc.

Monday, September 29, 2008 7:38:33 PM (Central Standard Time, UTC-06:00)  #    Comments [2]. Trackback 
Tags: .NET | Lambda Expressions | NAnt | Rhino Mocks | Test Driven Development | Unit Testing

 Wednesday, July 09, 2008

Visual Studio 2008 installs .NET 3.5 SDK v6.0A, which is what NAnt 0.86 expects to run against – however, the .NET 3.5 SDK that is available for download, via the "Windows 2008 and .NET 3.5 SDK", is v6.1

It’s a small difference, but that difference will cause NAnt 0.86 to not be able to target .NET 3.5 if you are using the downloadable .NET 3.5 SDK. When you try to run NAnt 0.86 and have it target .NET 3.5 with the downloaded .NET SDK, you will get an “Object reference” error from NAnt and the build will fail immediately.

image

Page Brooks has the details of how to fix NAnt to work against the v6.1 SDK. It is an easy fix, but it requires that every machine running NAnt have the v6.1 SDK installed (and if you want your devs to be able to run NAnt from their local machine, you’ll need all of the developers to install the updated version of the SDK).

End Result:

If you want to automate your VS2008 project builds with NAnt, you should probably have every developer on your team to install the new version of the SDK and have NAnt target that version.

Wednesday, July 09, 2008 12:10:31 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: .NET | General | Management | NAnt | Scripting

Navigation
About Me
View Derick Bailey's profile on LinkedIn

Send mail to the author(s) Contact Me
Archive
<December 2008>
SunMonTueWedThuFriSat
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2008
Derick Bailey
Sign In
Statistics
Total Posts: 91
This Year: 91
This Month: 0
This Week: 0
Comments: 40
Themes
Pick a theme:
All Content © 2008, Derick Bailey
DasBlog theme 'Business' created by Christoph De Baene (delarou)