提问者:小点点

通过单元测试在C#中模拟创建多个文件


这更多的是一个噱头,因为我希望实现我的单元测试的完全覆盖。

这是关于FileFinder()方法的。此方法查找文件夹中的文件并创建相应的增量命名。

现在我被告知应该有可能(无论出于什么原因)999,998个文件可能在这个文件夹中。

因此,如果检测到有一个文件被调用,例如job_999999.something,那么它应该返回一个空字符串。

相应的代码段如下所示:

var allFiles = new List<string>();
allFiles.AddRange(Directory.GetFiles(SomeImagesPath));
allFiles.AddRange(Directory.GetFiles(TempFolderPathForSomething));

var allExistingFiles = allFiles.Select(x =>
                                              {
                                                 var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(x);
                                                 return fileNameWithoutExtension.TrimStart('V', 'v').TrimStart('S', 's', 'P', 'p', 'V', 'v', 'I', 'i');
                                              })
                                      .ToList();
                                      
var i = 0;
while (i < 1000000)
{
   i++;
   if (!allExistingFiles.Contains($"{i:000000}"))
      break;
   if (i == 999999)
      return string.Empty;
}

我通过单元测试覆盖了所有内容,只有if的情况对我来说是不可能的。

就我个人而言,我认为这也是毫无意义的,但是我想知道(没有给你命名之前和之后的功能)是否有一种方法通过单元测试来模拟100万个文件被创建?如果有可能的话,我是不是必须写一个把所有东西都拉回去的方法,或者根本就没有这种可能性?

还是一般都有办法“骗”这个代码点?


共2个答案

匿名用户

在这种情况下,您的方法被硬编码为使用system.io.directory。这意味着您必须针对实际的文件系统进行编码。这意味着当您进行单元测试时,它将会命中实际的文件系统。那可不理想。单元测试不应具有外部依赖项。

我们可以通过对抽象进行编码来修复这种情况。System.io.abstractions库已经为我们提供了预构建的抽象和具体实现。这允许我们选择何时使用真实的文件系统(在正常运行时),何时使用文件系统的模拟(在单元测试期间)。

readonly IFileSystem _fileSystem;

// I'm assuming you use Dependency Injection.
// To hit the real system, your app should inject a System.IO.Abstractions.FileSystem.
// To test, you can pass in a System.IO.Abstractions.TestingHelpers.MockFileSystem
public MyClass(IFileSystem fileSystem)
{
    _fileSystem = fileSystem;
}

public string FileFinder()
{
    var allFiles = new List<string>();
    allFiles.AddRange(_fileSystem.Directory.GetFiles(SomeImagesPath));
    allFiles.AddRange(_fileSystem.Directory.GetFiles(TempFolderPathForSomething));

    var allExistingFiles = allFiles.Select(x =>
                                                  {
                                                     var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(x);
                                                     return fileNameWithoutExtension.TrimStart('V', 'v').TrimStart('S', 's', 'P', 'p', 'V', 'v', 'I', 'i');
                                                  })
                                          .ToList();
                                          
    var i = 0;
    while (i < 1000000)
    {
       i++;
       if (!allExistingFiles.Contains($"{i:000000}"))
          break;
       if (i == 999999)
          return string.Empty;
    }
}

然后您的测试变为:

[UnitTest]
public void MyTest()
{
    //arrange
    var mockFileSystem = new System.IO.Abstractions.TestingHelpers.MockFileSystem();
    //add fake files as needed to mockFileSystem
    var myClass = new MyClass(mockFileSystem);
    
    //act
    string result = myClass.FileFinder();
    
    //assert
    //make assertions here about your result
    
}

匿名用户

allexistingfiles列表创建移动到一个返回list的方法中,然后您只需要模拟它并为您的测试场景返回不同的列表--包含job_99999.something的列表,不包含它的列表,等等。
在单元测试流中添加某种机制来生成这些列表,使它成为集成测试,而不是单元测试,因为它打破了测试代码单元的想法。
连接应用程序与外部API(文件系统、数据库等)的所有内容都不是单元测试的主题。(如果您愿意,您仍然可以自动化它的测试,但这是一个不同的主题)。