Directory.GetFiles的单元测试条件

时间:2017-07-20 14:00:28

标签: c# unit-testing

我之前从未使用过单元测试,但我希望这样做能够继续前进。现在我有一个我想测试的最简单的方法:

public void GetAvailableFiles(string rootFolder)
{
   string[] dirs = Directory.GetFiles(rootFolder);
}

我的单元测试如下:

[Test]
public void CheckDirectory()
{     
   AvailableFile fileUpload = new AvailableFile();
   fileUpload.GetAvailableFiles("C:\\Input");      
}

当我调试它时,我返回目标位置中的所有文件。但是,我正在努力想出一个有效的条件,在这个条件下,这个测试可以验证它已经通过了。

我的想法是为此测试提供预期的文件数。我有10个文件,所以我应该期待10个结果。但是,我不确定如何用我所拥有的东西来写这个。

我应该怎么做?

3 个答案:

答案 0 :(得分:7)

这不是单元测试。这是一个集成测试,因为您与文件系统进行交互。如果要进行单元测试,则必须将文件系统交互提取到接口。例如:

public interface IFileScanner {
   List<string> GetAvailableFiles(string folder);
}

[Test]
public void GetAvailableFiles_EmptyFolder_ReturnsEmptyList()
{     
   // Arrange
   IFileScanner scanner = new FileScannerEmptyFolderStub();

   // Act
   var list = scanner.GetAvailableFiles("dummy argument");

   // Assert
   Assert.IsTrue(list.Count == 0);
}

我建议您在编写单元测试之前阅读本书The art of unit testing with examples (Roy Osherove)中的一些理论。

答案 1 :(得分:1)

为方法提供一些临时目录,其中包含一些文件:

public class TestClass : IDisposable
{
    private string _directory;

    public TestClass()
    {
        _directory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
        Directory.CreateDirectory(_directory);
    }

    [Fact]
    public void NoFiles()
    {
        Assert.Empty(GetAvailableFiles(_directory));
    }

    [Fact]
    public void TwoFiles()
    {
        File.WriteAllText(Path.Combine(_directory, "aaa.txt"), "");
        File.WriteAllText(Path.Combine(_directory, "bbb.txt"), "");

        var files = GetAvailableFiles(_directory);
        Assert.Equal(2, files.Length);
        Assert.Contains(Path.Combine(_directory, "aaa.txt"), files);
        Assert.Contains(Path.Combine(_directory, "bbb.txt"), files);
    }

    public void Dispose()
    {
        Directory.Delete(_directory, true);
    }

    // Method under test
    public string[] GetAvailableFiles(string rootFolder)
    {
        return Directory.GetFiles(rootFolder);
    }
}

这个测试很快,不需要任何依赖,因此虽然它不是严格的单元测试,但它没有带来许多通常与集成测试相关的缺点(依赖于外部数据库等)。 。)

如果此方法包含大量其他逻辑(例如,使用这些文件的内容),那么在该逻辑和文件系统访问之间引入接缝是一个好主意。

答案 2 :(得分:1)

正在测试的目标方法与静态实现问题紧密相关(Directory)。

假设目标SUT

public class AvailableFile {
    public void GetAvailableFiles(string rootFolder) {
       string[] files = Directory.GetFiles(rootFolder);
       //...other code using files
    }
}

将其提取为显式服务依赖项。

public interface IDirectory {
    string[] GetFiles(string rootFolder);
}

重构SUT

public class AvailableFile {
    private readonly IDirectory directory;

    public AvailableFile (IDirectory directory) {
        this.directory = directory;
    }

    public void GetAvailableFiles(string rootFolder) {
       string[] files = directory.GetFiles(rootFolder);
       //...other code using files
    }
}

生产中的服务实现可以像

那样完成
public class DirectoryServie : IDirectory  {
    public string[] GetFiles(string rootFolder) {
        return Directory.GetFiles(rootFolder);
    }
}

确保在合成根目录中向IoC容器注册了接口和实现。

完成所有解耦后,您现在可以单独测试依赖于这些服务的实现。你真的想要测试你控制的代码而不是第三部分实现,这就是Directory。微软会对此进行足够的测试。

使用Moq,您可以独立测试目标系统,而无需考虑实现问题。

[Test]
public void CheckDirectory_Should_GetFiles() {
    //Arrange
    var files = new [] { 
        "fake path 1",
        "fake path 2",
        //fake path n
    };
    var rootPath = "C:\\Input";
    var service = new Mock<IDirectory>();
    service.Setup(_ => _.GetFiles(rootPath).Returns(files).Verifiable();

    var fileUpload = new AvailableFile(service.Object);

    //Act   
    fileUpload.GetAvailableFiles(rootPath);

    //Assert
    service.Verify();
}