【问题标题】:Unit Testing code that doesn't execute immediately不会立即执行的单元测试代码
【发布时间】:2009-03-10 02:48:40
【问题描述】:

我正在使用 C# 3.0 和 NUnit。我想知道是否有一种标准方法可以对在一段时间后执行的代码执行单元测试。例如,我有一个简单的静态类,我可以向它注册方法,并在 n 毫秒后调用它们。我需要确保正在调用委托方法中的代码。

例如,以下测试将始终通过,因为在方法退出之前没有任何断言。

[Test]
public void SampleTest()
{
    IntervalManager.SetTimeout(delegate{ 
        Assert.Equals(now.Millisecond + 100, DateTime.Now.Millisecond); 
    }, 100);
}

甚至可以对不立即执行的代码进行单元测试吗?

干杯,

保罗

【问题讨论】:

    标签: c# unit-testing c#-3.0 nunit xna


    【解决方案1】:

    这个怎么样?它会导致测试阻塞一段预期的最长时间,以便回调触发并在保释之前完成并报告错误。

    public void Foo() {
        AutoResetEvent evt = new AutoResetEvent(false);
        Timer t = new Timer(state => {
            // Do work
            evt.Set();
        }, null, 100, Timeout.Infinite);
        if (evt.WaitOne(500)) {
            // method called and completed
        } else {
            // timed out waiting
        }
    }
    

    【讨论】:

    • 单元测试应该很快。在你的测试中引入定时等待会减慢速度——如果你有任何可观数量的这样的测试,那么每次测试 1/2 秒就太慢了。我建议使用模拟重构解决方案是处理此问题的更好方法。
    • 半秒是应该最小化的任意时间,我同意。在单元测试期间等待另一个线程完成工作的原则仍然有效,无论该线程是否在计时器上。但我同意,单元测试期间的定时等待会减慢速度,应该避免。
    【解决方案2】:

    那么你到底在测试什么?你在测试定时器的工作吗?或者您的代码正确设置了一个计时器,以便在到期时计时器执行回调?在不知道代码是什么样子的情况下,我假设您真正想要测试的是后者。我的回答是(1)这对于静态方法可能会很困难,(2)你可能需要使用依赖注入并注入模拟计时器等,它们实际上并不运行生成的方法,而是记录通过期望您的代码进行了正确的调用。

    【讨论】:

      【解决方案3】:

      作为旁注。我们通常会尝试使用NUnit category 标记运行缓慢的测试,并且可以选择在某些构建中跳过这些测试。

      【讨论】:

        【解决方案4】:

        是的,像你的例子那样做是行不通的。

        相反,我建议您创建一个测试类,用作委托,记录其方法被调用的时间和时间。

        然后您将您的模拟注入您要测试的 IntervalManager。然后您的测试方法必须等待 IntervalManager(使用 IntervalManager 提供的合适方法,或者等待几秒钟),然后您可以验证测试类的状态。

        顺便说一句,这种方法通常称为mocking;在这种情况下,测试类将是模拟对象。

        【讨论】:

        • 我如何等待几秒钟并验证测试类的状态?使用老式的 sleep() hack?
        • 好吧,只需使用标准库中的 sleep() 或等效函数(不知道 C# 中的名称)。为什么那会是黑客行为?您只是在等待记录该方法所需的时间。
        【解决方案5】:

        正如其他几个答案所表明的那样,长时间运行的测试通常不是一个好主意。为了便于测试这个组件,您应该考虑到您确实有两个不同的东西要测试。

        1. 当您注册定时委托执行时,会设置正确的时间。这可能需要使用超时和代表数量的各种组合进行测试。
        2. 委托以正确的方式执行。

        以这种方式分离测试将允许您测试您的计时机制是否按预期工作,只需少量短超时(测试您需要考虑的所有情况)。请记住,根据系统上的当前负载以及组件代码的复杂程度(即在IntervalManager 中),执行给定委托所需的实际时间可能需要一点余地。

        【讨论】:

          【解决方案6】:

          当然你可以测试它。您只需要等待它执行即可。

          【讨论】:

            【解决方案7】:

            也许我遗漏了一些东西,但 Visual Studio 的单元测试具有特殊属性,您可以将其放在控制执行顺序和其他内容的方法上。这应该在您第一次制作单元测试项目时自动生成:

                #region Additional test attributes
                // 
                //You can use the following additional attributes as you write your tests:
                //
                //Use ClassInitialize to run code before running the first test in the class
                //[ClassInitialize]
                //public static void MyClassInitialize(TestContext testContext) {
                //}
                //
                //Use ClassCleanup to run code after all tests in a class have run
                //[ClassCleanup()]
                //public static void MyClassCleanup()
                //{
                //}
                //
                //Use TestInitialize to run code before running each test
                //[TestInitialize()]
                //public void MyTestInitialize()
                //{
                //}
                //
                //Use TestCleanup to run code after each test has run
                //[TestCleanup()]
                //public void MyTestCleanup()
                //{
                //}
                //
                #endregion
            

            因此,使用 [ClassInitialize] 应该允许您将必须首先执行的任何内容写入方法中。然后你的测试就可以运行了。或者您可以使用 [TestInitialize] 在每次测试之前运行代码。

            【讨论】:

              猜你喜欢
              • 2016-11-26
              • 2020-12-08
              • 2022-11-06
              • 2022-01-23
              • 2014-09-05
              • 1970-01-01
              • 1970-01-01
              • 2016-01-01
              • 1970-01-01
              相关资源
              最近更新 更多