上述示例的问题在于parseFile方法创建了自己的StreamReader实例,这意味着模拟StreamReader是无法正常工作的,因为:
1. 您无法访问该对象。
2. 您只能模拟虚拟类或标记为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))
{
}
}
}
现在您可以模拟IFileManager
接口及其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();
}
}
}
Do something
提取成一个接受TextReader
参数的函数吗?如果可以的话,你可以使用StringReader
进行测试,而parseFile
则传递构造的StreamReader
。 - LeeDEBUG
)时,可以以多种不同的方式提供特定的数据集。该指令可能位于using
块之前,并且只有在不处于该模式时才进入using
块以访问文件系统。但需要注意的是:您不会真正测试文件系统访问部分,因此这会导致缺少一些测试场景。请记住这一点。 - gravity