Stopwatch dependency for unit-testing My AsyncDetector is internally using a Stopwatch to calculate time intervals. Because of this hardcoded dependency I am not able to write good unit-tests for it. So I must be able to use my own timestamps that I can use for assertions.

To do this I defined the IStopwatch interface that is a simplified version of the Stopwatch. Some of the properties I never use and my inteface does not provide them.

public interface IStopwatch
{
    bool IsRunning { get; }
    TimeSpan Elapsed { get; }
    IStopwatch Start();
    IStopwatch Stop();
    IStopwatch Restart();
    IStopwatch Reset();
}


Its default implementation is using of course the original Stopwatch so I provide it just for reference:


public class SystemStopwatch : IStopwatch
{
  private readonly Stopwatch _stopwatch;

  public SystemStopwatch()
  {
      _stopwatch = new Stopwatch();
  }

  public bool IsRunning =&gt; _stopwatch.IsRunning;

  public TimeSpan Elapsed =&gt; _stopwatch.Elapsed;

  public static IStopwatch StartNew()
  {
      return new SystemStopwatch().Start();
  }

  public IStopwatch Start()
  {
      _stopwatch.Start();
      return this;
  }

  public IStopwatch Stop()
  {
      _stopwatch.Stop();
      return this;
  }

  public IStopwatch Restart()
  {
      _stopwatch.Restart();
      return this;
  }

  public IStopwatch Reset()
  {
      _stopwatch.Restart();
      return this;
  }

  public override string ToString()
  {
      return _stopwatch.Elapsed.ToString();
  }
}



More interesing is the DebugStopwatch that will enumerate my timestamps on each access to the Elapsed property or it'll throw if there are not enough of them:

public class DebugStopwatch : IStopwatch
{
    private readonly IEnumerable&lt;TimeSpan&gt; _timestamps;
    private TimeSpan? _lastElapsed;
    private IEnumerator&lt;TimeSpan&gt; _enumerator;

    public DebugStopwatch([NotNull] IEnumerable&lt;TimeSpan&gt; elapses)
    {
        _timestamps = elapses ?? throw new ArgumentNullException(nameof(elapses));
    }

    public bool IsRunning { get; private set; }

    public TimeSpan Elapsed
    {
        get
        {
            if (IsRunning)
            {
                if (_enumerator.MoveNext())
                {
                    return (_lastElapsed = _enumerator.Current).Value;
                }

                throw new InvalidOperationException("You did not define enough timestamps.");
            }

            return _enumerator?.Current ?? _lastElapsed ?? TimeSpan.Zero;
        }
    }

    public static IStopwatch StartNew(IEnumerable&lt;TimeSpan&gt; elapses)
    {
        return new DebugStopwatch(elapses).Start();
    }

    public IStopwatch Start()
    {
        if (IsRunning)
        {
            return this;
        }
        _enumerator = _timestamps.GetEnumerator();
        IsRunning = true;
        return this;
    }

    public IStopwatch Stop()
    {
        IsRunning = false;
        return this;
    }

    public IStopwatch Restart()
    {
        Stop();
        Reset();
        Start();
        return this;
    }

    public IStopwatch Reset()
    {
        _enumerator = _timestamps.GetEnumerator();
        return this;
    }

    public override string ToString()
    {
        return (_lastElapsed ?? TimeSpan.Zero).ToString();
    }
}


Example Using it is very easy. I just need to provide a collection or enumeration with timestamps:

void Main()
{

    var stopwatch = DebugStopwatch.StartNew(Enumerable.Range(0, 10).Select(i =&gt; TimeSpan.FromSeconds(i)));

    stopwatch.Dump();
    stopwatch.Dump();
    stopwatch.Dump();

    stopwatch.Stop().Dump(nameof(IStopwatch.Stop));
    stopwatch.Dump();
    stopwatch.Dump();

    stopwatch.Reset().Dump(nameof(IStopwatch.Reset));
    stopwatch.Dump();
    stopwatch.Dump();
}




What do you think of the DebugStopwatch? Would you change anything about it? If so, how would you improve it?
I would not return IStopwatch from Start/Stop/etc. This IStopwatch is not an immutable Value Object, like String for example: it would be easier to set expectations by having just this:

public interface IStopwatch
{
    bool IsRunning { get; }
    TimeSpan Elapsed { get; }
    void Start();
    void Stop();
    void Restart();
    void Reset();
}


SystemStopwatch would be a way shorter:

class SystemStopwatch : IStopwatch
{
    Stopwatch Stopwatch { get; } = new Stopwatch();
    public bool IsRunning =&gt; Stopwatch.IsRunning;
    public TimeSpan Elapsed =&gt; Stopwatch.Elapsed;
    public void Start() =&gt; Stopwatch.Start();
    public void Stop() =&gt; Stopwatch.Stop();
    public void Reset() =&gt; Stopwatch.Reset();
    public void Restart() =&gt; Stopwatch.Restart();
}


As for the MockStopwatch (sounds better than Debug one for me) – it could be a little bit shorter:

class MockStopwatch : IStopwatch
{
    public MockStopwatch(params TimeSpan[] intervals)
        : this(intervals.AsEnumerable())
    {
    }

    public MockStopwatch(IEnumerable&lt;TimeSpan&gt; intervals)
    {
        Interval = intervals
            .Prepend(TimeSpan.Zero)
            .ToList()
            .GetEnumerator();

        Reset();
    }

    IEnumerator&lt;TimeSpan&gt; Interval { get; }
    public bool IsRunning { get; private set; }
    public TimeSpan Elapsed =&gt;
        !IsRunning
        ? Interval.Current
        : Interval.MoveNext()
            ? Interval.Current
            : throw new InvalidOperationException("You did not define enough timestamps.");

    public void Start() =&gt; IsRunning = true;
    public void Stop() =&gt; IsRunning = false;

    public void Restart()
    {
        Stop();
        Reset();
        Start();
    }

    public void Reset()
    {
        Interval.Reset();
        Interval.MoveNext();
    }
}


Where you might need to define Prepend (.net core LINQ has one already):

static class PrependAppend
{
    public static IEnumerable&lt;T&gt; Prepend&lt;T&gt;(this IEnumerable&lt;T&gt; source, T item) =&gt;
        Enumerable.Concat(new[] { item }, source);

    public static IEnumerable&lt;T&gt; Append&lt;T&gt;(this IEnumerable&lt;T&gt; source, T item) =&gt;
        Enumerable.Concat(source, new[] { item });
}


Updated Elapsed

What do you say about this one? Looks cleaner for me.

    public TimeSpan Elapsed =&gt; IsRunning ? NextElapsed : SameElapsed;
    TimeSpan SameElapsed =&gt; Interval.Current;
    TimeSpan NextElapsed =&gt; Interval.MoveNext() 
        ? Interval.Current 
        : throw new InvalidOperationException("You did not define enough timestamps.");