如何先开发一个StopWatch类测试?

发布于 2024-09-16 03:35:46 字数 2116 浏览 7 评论 0原文

我目前正在尝试实现一个 StopWatch 类。界面类似于:

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

基本上我的应用程序需要使用 StopWatch,但出于测试目的,最好有一种人为修改 StopWatches 的方法。结果,这就是我让所有代码引用 IStopWatch 而不是 .NET 的 System.Stopwatch 的原因。

当我尝试开发这个测试优先时,我必须在编写测试后才为我的 StopWatch 类编写代码。我已经意识到我要做的测试不会是单元测试,因为我在内部使用 .NET 的 System.Stopwatch 类。

因此,根据我的理解,我现在唯一能做的就是如下所示的测试:

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

我知道我不能像单元测试那样频繁地运行它,但我认为对此我无能为力。我的方法正确还是我错过了什么?

谢谢

编辑

所以我最终遵循了 Quinn351 和 Sjoerd 的建议,并编写了一些使用 DateTimes 而不是 .NET 的 Stopwatch 类的代码:

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

这允许我制作其他具有两个日期的 getter/setter 的实现,这样我就可以使 GetTimeElapsed() 返回我想要的任何内容想。

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 StopWatches' 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

才能让你更想念 2024-09-23 03:35:46

为什么要“人为地修改秒表的结果”?!如果您要修改结果,测试的全部目的是什么?

至于测试,您还应该测试 stop 方法,并且应该测试秒数是否大约是分钟数的 60 倍。

您正在编写的类也可以在内部使用 StopWatch,然后根据请求修改结果(然后您可以随时返回到原始结果)。

PS:方法名称应该更短并且没有下划线。

Why would "artifially modifing a StopWatches' results"?! What's the whole purpose of testing if your going to modify your results?

As for testing you should also test the stop method and you should test if the amount of seconds is roughly 60 times more than the amount of minutes.

The class you are writing could also use StopWatch internally and then modify results as requested (then you can always go back to the original results).

PS: Method names should be shorter and not have underlines.

葬﹪忆之殇 2024-09-23 03:35:46

您的秒表类可能从某个地方获取日期和时间。创建一个返回当前日期的对象。这将是一个非常简单的课程。测试时,传入一个模拟对象,它返回一个假的日期和时间。这样,您就可以在测试中假装已经过去了很多秒。

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

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);
    }
}
半寸时光 2024-09-23 03:35:46

一个问题是秒表在具有可变时钟速度的处理器(即最新的处理器)上不准确,如以下讨论所示

C# 秒表显示不正确的时间

因此,使用 Thread.Sleep 不会为您提供准确的时间。您最好使用 DateTime.Now 实现您自己的秒表版本

One problem is that Stopwatch is not accurate on processors with variable clock speeds i.e. most current ones, as can be seen with the following discussion

C# Stopwatch shows incorrect time

So using Thread.Sleep wont give you an accurate time. You would be better to implement your own version of Stopwatch using DateTime.Now

话少情深 2024-09-23 03:35:46

作为上面编辑的旁白:不要使用此方法来确定经过的时间!它在以下情况下无法正常工作:

  • 列出项目
  • 正在设置时钟或时区
  • 夏令时开始或结束
  • 时钟否则不可靠(例如在虚拟机内)

...并且可能还有许多其他条件。

As an aside to the EDIT above: Do not use this method to determine elapsed time! It does not work well when:

  • List item
  • the clock or timezone is being set
  • daylight savings starts or ends
  • the clock is otherwise unreliable (e.g. inside a VM)

... and probably a number of other conditions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文