如何模拟Directory.GetFiles?

Har*_*rry 4 .net c# unit-testing moq

我试图弄清楚Moq如何或是否可以执行以下操作

public class Download
{
    private IFoo ifoo;

    public Download(IFoo ifoo)
    {
        this.ifoo = ifoo;
    }

    public void Download()
    {
        var files = Directory.GetFiles("filepath"); //<<<===

        foreach (var item in files)
        {

            // do something

        }    
    }
}
Run Code Online (Sandbox Code Playgroud)

在单元测试中。

// Arrange 

var mockFoo = new Mock<IFoo>();
mockFoo.setup( s => s.Bar()).returns(true);

var foo = new Foo(mockFoo.Object);

// Act
foo.Download()
Run Code Online (Sandbox Code Playgroud)

我如何模拟files变量,所以该方法使用模拟版本。这甚至是正确的方法吗?因为我不是在模拟类,而是在模拟依赖关系,所以我该如何设置files变量,以便它查看模拟的文件string []。

Sco*_*nen 6

您将需要依赖抽象来获取文件,而不是硬依赖于System.IO.Directory

public interface IFileProvider
{
    string[] GetFiles(string path);
}

public class PhysicalFileProvider : IFileProvider
{
    public string[] GetFiles(string path)
    {
        return Directory.GetFiles(path);
    }
}
Run Code Online (Sandbox Code Playgroud)

您将以与注入完全相同的方式注入抽象IFoo。现在,您可以IFileProvider使用Moq进行模拟,创建一个模拟,该模拟将完全返回您要返回的字符串。

var fileProvider = new Mock<IFileProvider>();
fileProvider.Setup(x => x.GetFiles(It.IsAny<string>()))
    .Returns(new[] {"file1.txt", "file2.txt"});
Run Code Online (Sandbox Code Playgroud)

您还可以使用Microsoft.Extensions.FileProviders.Physical提供文件系统访问和抽象。

public class Download
{
    private readonly IFoo _foo;
    private readonly Microsoft.Extensions.FileProviders.IFileProvider _fileProvider;

    public Download(IFoo foo, IFileProvider fileProvider)
    {
        _foo = foo;
        _fileProvider = fileProvider;
    }

    public void SomethingWithFiles()
    {
        var files = _fileProvider.GetDirectoryContents("filepath")
            .Where(item => !item.IsDirectory);

        foreach (var item in files)
        {
            // something
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

具体的实现是PhysicalFileProvider


另一种变化。代替注入接口,注入委托:

public delegate string[] GetFilesFunction(string path);

public class Download
{
    private readonly IFoo _foo;
    private readonly GetFilesFunction _getFiles;

    public Download(IFoo foo, GetFilesFunction getFiles)
    {
        _foo = foo;
        _getFiles = getFiles;
    }

...
Run Code Online (Sandbox Code Playgroud)

这甚至更容易嘲笑。您甚至都不需要起订量。

var subject = new Download(mockedFoo, path => new []{"file1.txt","file2.txt"} );
Run Code Online (Sandbox Code Playgroud)