我需要测试一个在一段时间后执行一定量工作的方法。
while (running)
{
...
// Work
...
Thread.Sleep(Interval);
}
一个间隔时间被作为类的参数传递,所以我只需要传入0或1,但是如果不是这种情况,我想知道如何模拟系统时钟。
在我的测试中,我想简单地将时间向前推进Interval,并使线程唤醒。
我从未为执行线程编写过测试,我确信有一些要避免的问题 - 请随意详细说明您使用的方法。
谢谢!
我需要测试一个在一段时间后执行一定量工作的方法。
while (running)
{
...
// Work
...
Thread.Sleep(Interval);
}
一个间隔时间被作为类的参数传递,所以我只需要传入0或1,但是如果不是这种情况,我想知道如何模拟系统时钟。
在我的测试中,我想简单地将时间向前推进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()
。
我会创建一个单例服务,可以在应用程序测试时注入。这个单例服务必须包含方法,允许一些外部调用者(比如单元测试)能够取消睡眠操作。
或者,你可以使用 Mutex
或 WaitHandle
对象的 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...
Thread.Sleep()
的方法。我不建议使用 WaitHandle,除非您有其他代码可以指导线程何时应该唤醒。 - Dan BryantThread.Sleep()
作为一般实践更昂贵。然而,在单个约束场景中,这可能是有意义的——特别是如果您使用可注入服务(如我所提到的)仅在测试时才这样做。对于发布代码,人们可能会使用更轻量级的Sleep()
调用。 - LBushkin