繁体   English   中英

C#单元测试 - Thread.Sleep(x) - 如何模拟系统时钟

[英]C# Unit Testing - Thread.Sleep(x) - How to Mock the System Clock

我必须测试一个间隔后做一定工作量的方法。

while (running)
{
    ...
    // Work
    ...
    Thread.Sleep(Interval);
}

Interval作为参数传递给类,所以我可以传入0或1,但我对如何模拟系统时钟感兴趣,如果不是这样的话。

在我的测试中,我希望能够通过TimeSpan Interval简单地设置时间并让线程唤醒。

我从来没有为之前对执行线程起作用的代码编写测试,我确信有一些缺陷要避免 - 请随意详细说明你使用的方法。

谢谢!

如果您不希望测试线程实际休眠的事实,那么更直接的方法(也是可能的方法)就是拥有一个ISleepService。 然后你可以模拟它,然后不在你的测试中睡觉,但有一个实现确实会导致生产代码中的Thread.Sleep。

ISleepService sleepService = Container.Resolve<ISleepService>();

..

while (running)
{
    ...
    // Work
    ...
    sleepService.Sleep(Interval);
}

使用Moq的示例:

    public interface ISleepService
    {
        void Sleep(int interval);
    }

    [Test]
    public void Test()
    {
        const int Interval = 1000;

        Mock<ISleepService> sleepService = new Mock<ISleepService>();
        sleepService.Setup(s => s.Sleep(It.IsAny<int>()));
        _container.RegisterInstance(sleepService.Object);

        SomeClass someClass = _container.Resolve<SomeClass>();
        someClass.DoSomething(interval: Interval);

        //Do some asserting.

        //Optionally assert that sleep service was called
        sleepService.Verify(s => s.Sleep(Interval));
    }

    private class SomeClass
    {
        private readonly ISleepService _sleepService;

        public SomeClass(IUnityContainer container)
        {
            _sleepService = container.Resolve<ISleepService>();
        }

        public void DoSomething(int interval)
        {
            while (true)
            {
                _sleepService.Sleep(interval);
                break;
            }
        }
    }

更新

在设计\\维护说明中,如果更改“SomeClass”的构造函数是痛苦的,或者将依赖注入点添加到类的用户,那么服务定位器类型模式可以在这里提供帮助,例如:

private class SomeClass
{
    private readonly ISleepService _sleepService;

    public SomeClass()
    {
        _sleepService = ServiceLocator.Container.Resolve<ISleepService>();
    }

    public void DoSomething(int interval)
    {
        while (true)
        {
            _sleepService.Sleep(interval);
            break;
        }
    }
}

你无法真正模拟系统时钟。

如果你需要能够改变这样的代码的挂起行为,你需要重构它,这样你就不会直接调用Thread.Sleep()

我会创建一个单例服务,它可以在测试时注入应用程序。 单例服务必须包括允许某些外部调用者(如单元测试)能够取消睡眠操作的方法。

或者,您可以使用具有超时参数的MutexWaitHandle对象的WaitOne()方法。 这样你可以触发互斥锁取消“睡眠”或让它超时:

public WaitHandle CancellableSleep = new WaitHandle(); // publicly available

// in your code under test use this instead of Thread.Sleep()...
while( running ) {
    // .. work ..
    CancellableSleep.WaitOne( Interval ); // suspends thread for Interval timeout
}


// external code can cancel the sleep by doing:
CancellableSleep.Set(); // trigger the handle...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM