【问题标题】:How do I write a unit test that relies on file system events?如何编写依赖文件系统事件的单元测试?
【发布时间】:2012-08-30 20:13:08
【问题描述】:

我想测试以下代码:

public class DirectoryProcessor
{
    public string DirectoryPath
    {
        get;
        set;
    }

    private FileSystemWatcher watcher;

    public event EventHandler<SourceEventArgs> SourceFileChanged;

    protected virtual void OnSourceFileChanged(SourceEventArgs e)
    {
        EventHandler<SourceEventArgs> handler = SourceFileChanged;
        if(handler != null)
        {
            handler(this, e);
        }
    }

    public DirectoryProcessor(string directoryPath)
    {
        this.DirectoryPath = directoryPath;
        this.watcher = new FileSystemWatcher(directoryPath);
        this.watcher.Created += new FileSystemEventHandler(Created);
    }

    void Created(object sender, FileSystemEventArgs e)
    {
        // process the newly created file
        // then raise my own event indicating that processing is done
        OnSourceFileChanged(new SourceEventArgs(e.Name));
    }
}

基本上,我想编写一个 NUnit 测试来执行以下操作:

  1. 创建目录
  2. 设置DirectoryProcessor
  3. 将一些文件写入目录(通过File.WriteAllText()
  4. 检查DirectoryProcessor.SourceFileChanged 是否已为步骤 3 中添加的每个文件触发一次。

我尝试这样做并在第 3 步之后添加 Thread.Sleep(),但很难让超时正确。它正确处理了我写入目录的第一个文件,但不是第二个(超时设置为 60 秒)。即使我能让它以这种方式工作,这似乎也是一种糟糕的测试方式。

有没有人能很好的解决这个问题?

【问题讨论】:

    标签: c# events filesystemwatcher


    【解决方案1】:

    通常,您关心的是测试与文件系统的交互,而无需测试实际执行操作的框架类和方法。

    如果您在类中引入抽象层,则可以在单元测试中模拟文件系统,以验证交互是否正确,而无需实际操作文件系统。

    在测试之外,“真正的”实现会调用这些框架方法来完成工作。

    是的,理论上您需要对“真实”实现进行集成测试,但实际上它应该是低风险的,不会发生太大变化,并且可以通过几分钟的手动测试来验证。如果您使用开源文件系统包装器,它可能会包含这些测试以让您安心。

    How do you mock out the file system in C# for unit testing?

    【讨论】:

    • 那么,在这里你建议模拟出 filesystemwatcher 类?
    【解决方案2】:

    如果您要测试使用此类的另一个对象,我的答案是不相关的。

    当我为操作编写单元测试时,我更喜欢使用 ManualResetEvent

    单元测试类似于:

         ...
         DirectoryProcessor.SourceFileChanged+=onChanged;
         manualResetEvent.Reset();
         File.WriteAllText();
         var actual = manualResetEvent.WaitOne(MaxTimeout);
         ...
    

    当 manualResetEvent 是 ManualResetEvent 并且 MaxTimeout 是某个 TimeSpan 时(我的建议总是使用超时)。 现在我们缺少“onChanged”:

         private void onChanged(object sender, SourceEventArgs e)
         {
              manualResetEvent.Set();
         }    
    

    希望对你有帮助

    【讨论】:

    • 谢谢!我不熟悉 ManualResetClass!总的来说,我认为文件系统模拟是进行单元测试的正确方法。我也喜欢编写这些更集成的测试,让我了解代码是如何工作的。
    猜你喜欢
    • 1970-01-01
    • 2010-09-12
    • 1970-01-01
    • 2010-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多