c# - 在 C# 中模拟 StreamReader 以进行单元测试

标签 c# unit-testing moq

我有这种格式的代码:

public void parseFile(string filePath)
{
    using (var reader = new StreamReader(@filePath))
    {
        //Do something
    }
}

我现在想对我的代码进行单元测试,但不希望我的单元测试实际访问文件系统,因为如果实际文件不存在,这会使它们变得不可靠。

为了防止 streamreader 访问文件系统,我需要模拟它。我的理解是我只能模拟一个实现接口(interface)的类。

所以我看到的一个解决方案是为 Streamreader 创建一个包装类和接口(interface),然后我可以模拟它。

我的问题分为两部分:

  1. 这是解决这个问题的唯一方法吗?

  2. 为我的项目添加一个额外的层以促进单元测试是否正确?如果我在全局范围内遵循这一原则,我可以添加很多额外的类(class)吗?

最佳答案

上面示例的问题在于 parseFile 方法正在创建它自己的 StreamReader 实例,因此模拟 StreamReader 不会它实际上不起作用,因为:

  1. 您无权访问该对象。
  2. 您只能模拟接口(interface) 或标记为虚拟 的类的成员。

您可以做的是创建一个接口(interface),为了参数起见,我们将其称为 IFileManager,并使用一个名为 StreamReader 的方法。

public interface IFileManager
{
     StreamReader StreamReader(string path);
}

然后在包含您在上面发布的 ParseFile 方法的其他类(我们称它为 Foo)中:

public class Foo 
{
    IFileManager fileManager;

    public Foo(IFileManager fileManager)
    {
        this.fileManager = fileManager;
    }

    public void parseFile(string filePath)
    {
        using (var reader = fileManager.StreamReader(filePath))
        {
            //Do something
        }
    }
}

现在你可以模拟 IFileManager interface 及其 StreamReader 方法,你可以将这个模拟实例注入(inject)到 Foo 类使其可供 ParseFile 方法使用。

现在您的代码将依赖于抽象而不是具体实现,我们已经反转了依赖性,这使我们能够模拟依赖性并隔离我们想要实际测试的代码。


创建模拟对象的粗略演示

public static void Main()
{
    Mock<IFileManager> mockFileManager = new Mock<IFileManager>();
    string fakeFileContents = "Hello world";
    byte[] fakeFileBytes = Encoding.UTF8.GetBytes(fakeFileContents);

    MemoryStream fakeMemoryStream = new MemoryStream(fakeFileBytes);

    mockFileManager.Setup(fileManager => fileManager.StreamReader(It.IsAny<string>()))
                   .Returns(() => new StreamReader(fakeMemoryStream));

    Foo foo = new Foo(mockFileManager.Object);

    string result = foo.ParseFile("test.txt");

    Console.WriteLine(result);
}

public interface IFileManager
{
    StreamReader StreamReader(string path);
}

public class Foo
{
    IFileManager fileManager;

    public Foo(IFileManager fileManager)
    {
        this.fileManager = fileManager;
    }

    public string ParseFile(string filePath)
    {
        using (var reader = fileManager.StreamReader(filePath))
        {
            return reader.ReadToEnd();
        }
    }
}

关于c# - 在 C# 中模拟 StreamReader 以进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55498470/

相关文章:

unit-testing - 在单独的框架中即时对 Clojure 进行单元测试

c# - Moq 一个类并仍然使用它的方法

asp.net-mvc - MOQ:不能将 Moq.Mock 转换为 VB.Net 中的接口(interface)

c# - 使用 UnitOfWork 模拟上下文和存储库

c# - ASP.NET MVC 模型验证不适用于 ViewModel

swift - 如何测试所需的初始化(编码器 :)?

java - 如何为 native Selenium 方法编写单元测试用例

c# - 可视化 C# 方法逻辑

c# - 如何在 MVC 中托管多个 IoC 驱动的 WCF 服务?

c# - 调用特定网络场