devoured elysium devoured elysium -4 years ago 115
C# Question

How to develop a StopWatch class test first?

I'm currently trying to implement a

StopWatch
class. The interface is something like:

interface IStopWatch {
void Run();
void Stop();
int SecondsElapsed { get; }
int MinutesElapsed { get; }
}


Basically my app will need to use a
StopWatch
, but for testing purposes it'd be nice to have a way of artifially modifing a
StopWatch
es' results, so that is the reason I am making all my code reference an
IStopWatch
instead of .NET's
System.Stopwatch
.

As I'm trying to develop this Test-First, I'll have to make code for my
StopWatch
class only after writing its tests. I already realized that the tests I'm going to do aren't going to be Unit-Tests, as I'm using .NET's
System.Stopwatch
class internally.

So, in my undertanding, the only thing I can do now are tests that look like the following:

[TestMethod]
public void Right_After_Calling_Run_Elapsed_Minutes_Equals_Zero() {
IStopWatch stopWatch = new StopWatch();
stopWatch.Run();
Assert.AreEqual<int>(0, stopWatch.ElapsedMinutes);
}

[TestMethod]
public void One_Second_After_Run_Elapsed_Seconds_Equals_One() {
IStopWatch stopWatch = new StopWatch();
stopWatch.Run();
Thread.Sleep(1000 + 10);
Assert.AreEqual<int>(1, stopWatch.ElapsedSeconds);
}

[TestMethod]
public void Sixty_Seconds_After_Run_Elapsed_Minutes_Equals_One() {
IStopWatch stopWatch = new StopWatch();
stopWatch.Run();
Thread.Sleep(60 * 1000 + 1000);
Assert.AreEqual<int>(1, stopWatch.ElapsedMinutes);
}


I know I can't just run this as often as my Unit-Tests, but I think there is nothing I can do about this. Is my approach correct or am I missing something?

Thanks

Edit



So I ended up following both Quinn351 and Sjoerd's advices and coded something that uses DateTimes instead of .NET's Stopwatch class:

public class MyStopWatch : IMyStopWatch {
private DateTime? firstDateTime = null;
private DateTime? secondDateTime = null;
private bool isRunning = false;

public void Start() {
isRunning = true;
firstDateTime = DateTime.Now;
}

public void Stop() {
isRunning = false;
secondDateTime = DateTime.Now;
}

public void Reset() {
firstDateTime = null;
secondDateTime = null;
isRunning = false;
}

public TimeSpan GetTimeElapsed() {
return secondDateTime.Value.Subtract(firstDateTime.Value);
}
}


This allows me to make other implementations that have getter/setters for both dates so I can make GetTimeElapsed() return whatever I want.

Answer Source

Your stopwatch class probably gets the date and time from somewhere. Make an object which returns the current date. This would be a very simple class. When testing, pass in a mock object instead, which returns a fake date and time. This way, you can pretend in your test as if many seconds have elapsed.

class DateTime {
    public Date getDate() {
        return System.DateTime.Now();
    }
}

class MockDateTime {
    public Date getDate() {
        return this.fakeDate;
    }
}

class StopwatchTest {
    public void GoneInSixtySeconds() {
        MockDateTime d = new MockDateTime();
        d.fakeDate = '2010-01-01 12:00:00';
        Stopwatch s = new Stopwatch();
        s.Run();
        d.fakeDate = '2010-01-01 12:01:00';
        s.Stop();
        assertEquals(60, s.ElapsedSeconds);
    }
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download