辅助类的依赖注入还是静态类?

问题描述 投票:0回答:1

在我的大多数项目中,我通常创建一个静态类,其中包含我在项目中使用的所有函数。 但是当谈到单元测试时,我觉得我的设计有一个很大的缺陷,因为我不能使用 Moq 来 Mock 这个静态类

我可以提供一个简单的示例,其中包含我正在处理的一个项目,该项目复制了很多文件,因此我做了一个小助手来避免代码中出现冗余

public static class Helpers
{
    public static void CopyFile(string sourcePath, string destPath)
    {
        Log.Verbose("Start copy from {SourcePath} to {DestPath}", sourcePath, destPath);
        if (!Directory.Exists(Path.GetDirectoryName(destPath)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(destPath));
        }
        File.Copy(sourcePath, destPath, true);
        Log.Verbose("End of copy from {SourcePath} to {DestPath}", sourcePath, destPath);
    }
}

所以基本上任何调用此函数的代码都是不可测试的,因为我无法在也依赖于 System.IO 的静态类中模拟静态函数

我尝试使用 System.IO.Abstraction 和依赖注入来解决这个问题,然后我有以下代码:

public interface IHelpers
{
    public void CopyFile(string sourcePath, string destPath);
}

public class Helpers(IFileSystem fileSystem) : IHelpers
{
    private readonly IFileSystem FileSystem = fileSystem;

    public void CopyFile(string sourcePath, string destPath)
    {
        Log.Verbose("Start copy from {SourcePath} to {DestPath}", sourcePath, destPath);
        if (!FileSystem.Directory.Exists(Path.GetDirectoryName(destPath)))
        {
            FileSystem.Directory.CreateDirectory(Path.GetDirectoryName(destPath));
        }
        FileSystem.File.Copy(sourcePath, destPath, true);
        Log.Verbose("End of copy from {SourcePath} to {DestPath}", sourcePath, destPath);
    }
}

现在我只需要在需要调用 CopyFile 的地方注入 IHelper ,一切看起来都很好。 但我有一些罕见的情况,在类中注入这个助手感觉是错误的

public class Component
{
    public string Name;
    public string Path;

    public void ExportOutput()
    {
        // ...
        // much calculation then...
        // multiple calls to Helpers.CopyFile(src,dest);
        // ...
    }
}

为了解决这个问题,我将 IHelper 类作为参数传递给 ExportOutput,但这看起来绝对令人恶心。

我发现这个问题的唯一解决方案是删除 ExportOutput 函数并将其放在我可以依赖依赖注入来使用 Helper 类的地方

这是对的还是我完全没有抓住要点?

您通常如何处理代码中的那些小辅助函数?

c# design-patterns dependency-injection
1个回答
0
投票

如果您想模拟文件系统操作,您将需要一个抽象文件系统的接口,并将其注入到使用文件系统的任何地方。

您的方法的替代方法是创建

CopyFile
作为文件系统的扩展,即

public static class Helpers
{
    public static void CopyFile(this IFileSystem fileSystem, string sourcePath, string destPath)
    {
        ...
    }
}

此方法会将

CopyFile
实现视为所测试事物的一部分,根据具体情况,这可能是可取的,也可能不是可取的。一个可能的优点是,它允许被测试的方法提供自己的 CopyFile 实现,而无需更改依赖项或测试。

除非我有一些罕见的情况,在类中注入这个助手感觉是错误的

如果您的

ExportOutput
方法依赖于文件系统,并且您希望此依赖项可测试,则需要以某种方式注入此依赖项。我可能会在
Component
的构造函数中注入依赖项,或者将
ExportOutput
移动到它自己的类中。但在某些情况下,使用方法参数可能是合适的。

请注意,文件系统可能存在大量难以准确重现的怪癖,特别是考虑到 IO 操作可能失败的所有方式。因此,您可能希望至少进行一些使用实际文件系统的测试,即使这可能会慢得多。请记住事后清理所有剩余的文件。

© www.soinside.com 2019 - 2024. All rights reserved.