我有这种格式的代码:
public void parseFile(string filePath)
{
using (var reader = new StreamReader(@filePath))
{
//Do something
}
}
我现在想对代码进行单元测试,但不希望单元测试实际访问文件系统,因为如果实际文件不存在,这会使它们不可靠。
为了防止streamreader访问文件系统,我需要对其进行模拟。我的理解是,我只能模拟实现接口的类。
因此,我可以看到的一个解决方案是为其创建包装类和接口Streamreader
,然后可以对其进行模拟。
我的问题分为两个部分:
这是解决这个问题的唯一方法吗?
为了方便进行单元测试,在项目中添加额外的层是否正确?如果我在全球范围内遵循这一原则,我会增加很多额外的课程吗?
上面的示例的问题在于,该parseFile
方法正在创建自己的实例StreamReader
,因此模拟StreamReader
不会真正起作用,因为:
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
类中,以供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();
}
}
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句