Multithreading 单元测试线程类,避免测试中的Thread.Sleep()?
我正试图找出对这个类进行单元测试的最佳方法:Multithreading 单元测试线程类,避免测试中的Thread.Sleep()?,multithreading,unit-testing,nunit,sleep,thread-sleep,Multithreading,Unit Testing,Nunit,Sleep,Thread Sleep,我正试图找出对这个类进行单元测试的最佳方法: public class FileGroupGarbageCollector { private Task _task; private readonly AutoResetEvent _event = new AutoResetEvent(false); public void Start() { _task = Task.Factory.StartNew(StartCollecting);
public class FileGroupGarbageCollector
{
private Task _task;
private readonly AutoResetEvent _event = new AutoResetEvent(false);
public void Start()
{
_task = Task.Factory.StartNew(StartCollecting);
}
public void Stop()
{
_event.Set();
}
private void StartCollecting()
{
do
{
Process();
}
while (!_event.WaitOne(60000, false));
}
private void Process()
{
/* do some work to the database and file system */
}
}
它不应该是组织最完善的课程,只是想找出一些东西
然后我进行了一个单元测试,在这里我想启动然后停止服务,断言私有的“Processs”方法对数据库或文件系统做了什么
我的单元测试如下(nunit):
这里有没有什么方法或好的模式可以用来避免Thread.Sleep()?我讨厌在单元测试中睡觉(更不用说在生产代码中了),但我拒绝只测试私有功能!我想测试这个类的公共接口
非常感谢您的回答:)
回答后更新
我按照国际奥委会的方式行事,效果非常好:)
公共接口IEventFactory
{
IEvent Create();
}
然后我的模拟对象(使用Moq):
var mockEvent=new Mock();
var mockEventFactory=new Mock();
mockEvent.Setup(x=>x.WaitOne(It.IsAny())。返回(true);
mockEvent.Setup(x=>x.Set());
mockEventFactory.Setup(x=>x.Create()).Returns(mockEvent.Object);
因此,对IEvent.WaitOne()的调用立即返回true并退出,因此不需要使用Thread.Sleep()
:)基本上,这里必须应用控制反转模式。因为代码是高度耦合的,所以您在测试它时遇到了问题 您应该清楚地分离所有实体,并将它们放在相应的接口上。如果一个实体是通过接口使用的,那么很容易对它进行模拟
public interface ITaskFactory {}
public interface IThreadManager {}
public interface ICollectorDatabase {}
public interface IEventFactory {}
public class FileGroupGarbageCollector
{
ITaskFactory taskFactory;
IThreadManager threadManager;
ICollectorDatabase database;
IEventFactory events;
FileGroupGarbageCollector (ITaskFactory taskFactory,
IThreadManager threadManager, ICollectorDatabase database,
IEventFactory events)
{
// init members..
}
}
一旦隔离了所有依赖项,FileGroupGarbageCollector就不会直接使用它们中的任何一个。在您的测试中,IEventFactory模拟将返回事件,如果调用WaitOne方法,则该事件将不起任何作用。因此,代码中不需要任何睡眠
去寻找尽可能多的关于模拟、控制反转、依赖注入模式的信息。
线程。睡眠是设计拙劣程序的标志。但是,它在单元测试中非常有用
唯一的其他选择是改变“时间”的用法。Rx团队在这方面做了一些出色的工作。但这对您的特定情况没有帮助(除非您转换为Rx调度程序)
如果你真的想在单元测试中避免Thread.Sleep
,那么你需要提取出依赖于时间的部分(或者使用控制反转或者像微软Moles这样的拦截库);问题是很难对“时间”进行完整、一致的抽象。就我个人而言,在单元测试中使用Thread.sleep
,我不会失眠。为什么需要Thread.sleep()呢?你的测试要完成什么?你做得很好,亚当!正是我试图解释的!我对单元测试相当陌生,但在我的实际代码中尝试过使用IoC,但我想我可能做得还不够。简单地-抽象出等待的实体。在测试中,等待不会发生(因为它是模拟的),所以您不需要睡眠。我不会使用AutoResetEvent
“紧密耦合”的私有实例调用FileGroupGarbageCollector
。@alexanderb:IEventFactory(在最简单的级别上)是否会有Create()和WaitOne()方法?我对如何删除“time”感到困惑,因为对于任何形式的基于超时的WaitOne(),都需要指定一个timeout int?@Adam,如果我愿意的话-IEventFactory将有一个返回IEvent对象的方法Create(),该方法有WaitOne()方法。IEEvent对象被模拟(是的,这听起来可能有点吓人..但刚开始,你会看到它并没有那么复杂)“它们的调度程序都是可测试的”这实际上是不正确的。任何Rx操作员都会过载,以接受可测试的调度程序。但是,例如,调度程序
,与调度程序本身一样,不具有更高的可测试性。
public interface IEvent
{
bool WaitOne(int timeout);
void Set();
}
var mockEvent = new Mock<IEvent>();
var mockEventFactory = new Mock<IEventFactory>();
mockEvent.Setup(x => x.WaitOne(It.IsAny<int>())).Returns(true);
mockEvent.Setup(x => x.Set());
mockEventFactory.Setup(x => x.Create()).Returns(mockEvent.Object);
public interface ITaskFactory {}
public interface IThreadManager {}
public interface ICollectorDatabase {}
public interface IEventFactory {}
public class FileGroupGarbageCollector
{
ITaskFactory taskFactory;
IThreadManager threadManager;
ICollectorDatabase database;
IEventFactory events;
FileGroupGarbageCollector (ITaskFactory taskFactory,
IThreadManager threadManager, ICollectorDatabase database,
IEventFactory events)
{
// init members..
}
}