【问题标题】:How to mock Directory.getFiles in the same time make sure the production code run as well [closed]如何同时模拟 Directory.getFiles 确保生产代码也运行[关闭]
【发布时间】:2019-07-03 08:13:13
【问题描述】:

如何同时模拟 Directory.getFiles 以确保生产代码也能运行。例如,如果运行单元测试,它将运行模拟数据,如果在运行时生产中并且实际上已经传递了参数,它会调用 System.io.Directory。

【问题讨论】:

  • 如果我链接的问题没有回答你的问题,请在评论中标记我 (@john),我会重新打开你的问题。
  • @John 它只回答了如何进行单元测试,但没有显示是否正常运行代码并实际运行到目录并获取文件
  • 如果您在谈论System.IO.Abstractions 库,似乎有FileSystem 接口的具体实现IFileSystem。这对你来说足够了吗?
  • 可以大致告诉我如何制作吗? :(

标签: c# unit-testing system.io.file


【解决方案1】:

我的建议是您使用System.IO.Abstractions,可从 NuGet 安装。然后,您将代码编写到一个接口 IFileSystem,而不是直接编写到 System.IO FileDirectory 等对象。

所以无论你在哪里需要访问这些方法,你都需要注入IFileSystem,这意味着FileSystem的实例,暴露为IFileSystem必须在你的依赖注入控制器中注册,或者你需要实例化你的服务为new MyService(new FileSystem()),其中有一个构造函数参数采用IFileSystem。请注意,DI 是执行此操作的首选方式。

让我们创建一个返回当前目录中文件的简单服务:

public class MyService
{
    private readonly IFileSystem _fileSystem;

    public MyService(IFileSystem fileSystem)
    {
        this._fileSystem = fileSystem;
    }

    public string[] GetFileNames()
    {
        return _fileSystem.Directory.GetFiles(_fileSystem.Directory.GetCurrentDirectory());
    }
}

在这里你可以看到我们接受IFileSystem,它将被注入到我们的类中。我们的GetFileNames() 方法只是获取当前目录,获取其中的文件,然后返回它们。

现在让我们在生产代码中使用它:

// FileSystem should be registered with your dependency injection container,
// as should MyService. MyService should be resolved through the container
// and not manually instantiated as here.
var fileSystem = new FileSystem();
var service = new MyService(fileSystem);

var files = service.GetFileNames();
foreach (var file in files)
{
    Console.WriteLine(file);
}

嘿,presto,它会打印出项目构建文件夹的预期文件列表。

现在,我们如何测试它?在本示例中,我使用的是 xUnit 和 Moq。我也在我的单元测试项目中包含了System.IO.Abstractions NuGet 包。

首先,我们需要模拟IFileSystem 对象:

var mockDirectory = new Mock<IDirectory>();
// set up the GetCurrentDirectory() method to return c:\
mockDirectory.Setup(g => g.GetCurrentDirectory()).Returns(@"c:\");
// Set up the GetFiles method to return c:\test.txt and c:\test2.txt where the path passed is c:\
mockDirectory.Setup(g => g.GetFiles(It.Is<string>(@"c:\"))).Returns(new[] { @"c:\test.txt", @"c:\test2.txt" });

var mockFileSystem = new Mock<IFileSystem>();
// Set up IFileSystem's .Directory property to return the mock of IDirectory that we created above
mockFileSystem.SetupGet(g => g.Directory).Returns(mockDirectory.Object);

// Create an instance of the mock that we can use in our service
var fileSystem = mockFileSystem.Object;

现在要对其进行测试,我们只需将其传递给服务并调用方法:

var myService = new MyService(fileSystem);

var files = myService.GetFileNames();
var expected = new[] { @"c:\test.txt", @"c:\test2.txt" };

Assert.True(files.SequenceEqual(expected));

这将在GetFileNames 方法中使用我们模拟的IFileSystem 实现,因此我们可以对其进行测试。请注意,如果您使用不同的 GetFiles 重载,则需要模拟相关方法。

【讨论】:

  • 参考检查 stackoverflow.com/q/56862061/5233410stackoverflow.com/questions/56851035/… 。我将由您决定是否要继续沿着这条路走下去。
  • 永远不会建议不提供帮助。只是提供 OP 之前尝试的上下文。
  • 不用担心。吃得开心,编码愉快。
  • 我认为问题不在于测试。根据我对问题的理解,他们很难理解如何在生产中使用重构的代码。 (单元测试之外)
  • 语言障碍无助于解释清楚。
【解决方案2】:

您可以创建自己的包装类,例如

    public interface IFileWrapper
    {
        FileInfo[] GetFiles(DirectoryInfo di);
    }

    public class FileWrapper : IFileWrapper
    {
        public FileInfo[] GetFiles(DirectoryInfo di)
        {
            return di.GetFiles();
        }
    }

你应该能够模拟出结果。

或者,有一些可用的软件包可能已经有这个。

希望对你有帮助

【讨论】:

  • 在 I/O 被 wrap 之后,当我不做单元测试时,可以调用实际的 I/O 而是像往常一样运行它,并且有实际的参数传递认为它需要 system.io.directory获取文件。
  • 抱歉,阐述不佳
  • @MANCHUNLIEW 这就是依赖注入的用途。在测试中,您将注入一个模拟文件系统。在生产环境中运行时,您将注入实际的文件系统实现。
猜你喜欢
  • 1970-01-01
  • 2020-07-03
  • 1970-01-01
  • 2012-09-14
  • 1970-01-01
  • 1970-01-01
  • 2010-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多