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..
  }
}