UnitTesting一个线程类,避免测试中的Thread.Sleep()?

ada*_*iko 5 multithreading nunit unit-testing 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 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 */
    }
}
Run Code Online (Sandbox Code Playgroud)

它不应该是最完美的课程,只是想弄清楚什么!

然后我有一个单元测试,我想要启动然后停止服务,断言私有'Processs'方法对数据库或文件系统做了些什么.

我的单元测试如下(nunit):

    [Test]
    public void TestStart()
    {
        var fg = new FileGroupGarbageCollector(30000);

        fg.Start();

        Thread.Sleep(5000); // i hate this!

        fg.Stop();

        // assert it did what i wanted it to do!
    }
Run Code Online (Sandbox Code Playgroud)

有什么方法或任何好的模式可以在这里使用,所以我可以避免Thread.Sleep()?我讨厌睡在单元测试中的想法(更不用说在生产代码中),但我拒绝只测试私有功能!我想测试这个类的公共接口.

任何答案都非常感谢:)

回答后更新

我采用了IoC的方式,它的工作非常好:)

public interface IEventFactory {IEvent Create(); }

public interface IEvent
{
    bool WaitOne(int timeout);
    void Set();
}
Run Code Online (Sandbox Code Playgroud)

然后我的模拟对象(使用Moq):

 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);
Run Code Online (Sandbox Code Playgroud)

因此,立即调用IEvent.WaitOne()会返回true并退出,因此不需要Thread.Sleep()!

:)

Ale*_*sky 7

基本上,您必须在此处应用反转控制模式.因为代码是高度耦合的,所以测试它时遇到问题.

您应该明确区分所有实体并将它们放在相应的接口上.如果通过接口使用实体,则很容易模拟它.

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..
  }
}
Run Code Online (Sandbox Code Playgroud)

一旦所有依赖项被隔离,FileGroupGarbageCollector就不会直接使用它们中的任何一个.在您的测试中,IEventFactory mock将返回Event,如果调用WaitOne方法则不会执行任何操作.因此,您的代码中不需要任何睡眠.

尽可能多地去寻找 - 模拟,控制反转,依赖注入模式.