使用C#模拟StreamReader进行单元测试

时间:2019-04-03 15:08:37

标签: c# unit-testing moq

我有这种格式的代码:

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

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

为了防止流读取器访问文件系统,我需要对其进行模拟。我的理解是,我只能模拟实现接口的类。

所以我可以看到的一个解决方案是为Streamreader创建包装器类和接口,然后可以对其进行模拟。

我的问题分为两个部分:

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

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

2 个答案:

答案 0 :(得分:5)

上述示例的问题在于parseFile方法正在创建自己的StreamReader实例,因此模拟StreamReader实际上是行不通的,因为:

  1. 您无权访问该对象。
  2. 您只能模拟interfaces或标记为virtual的类的成员。

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

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

然后在另一个类(我们将其称为Foo)中,该类包含您上面发布的ParseFile方法:

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方法,您可以将此模拟实例注入Foo类中,以供{{1} }使用方法。

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


创建模拟对象的粗略演示

ParseFile

答案 1 :(得分:0)

看看System.IO.Abstractions-这几乎可以让您模拟用于测试的System.IO .net命名空间