C#unit testing – Thread.Sleep(x) – 如何模拟系统时钟

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

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

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

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

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

谢谢!

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

 ISleepService sleepService = Container.Resolve(); .. while (running) { ... // Work ... sleepService.Sleep(Interval); } 

使用Moq的示例:

  public interface ISleepService { void Sleep(int interval); } [Test] public void Test() { const int Interval = 1000; Mock sleepService = new Mock(); sleepService.Setup(s => s.Sleep(It.IsAny())); _container.RegisterInstance(sleepService.Object); SomeClass someClass = _container.Resolve(); 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(); } public void DoSomething(int interval) { while (true) { _sleepService.Sleep(interval); break; } } } 

更新

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

 private class SomeClass { private readonly ISleepService _sleepService; public SomeClass() { _sleepService = ServiceLocator.Container.Resolve(); } public void DoSomething(int interval) { while (true) { _sleepService.Sleep(interval); break; } } } 

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

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

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

或者,您可以使用具有超时参数的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...