如何使用.Net IO 类创建可测试代码?

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

我想创建可模拟对 .Net System.IO 类的调用的单元测试代码,这样我就可以真正进行单元测试,而不是依赖于文件系统。 我正在使用 SystemWrapper 类来包装 BCL 类。

我正在尝试获取一个简单的示例来查看文件是否存在。

我遇到的问题是,在类中注入依赖项不起作用,因为实例化依赖项(通过StructureMap)需要知道要传递什么构造函数参数,这在当时不可用,也没有默认构造函数。

示例代码:

// don't want to create dependency here like so
//IFileInfoWrap fileInfoWrap = new FileInfoWrap(filename);

// using service locator (anti-pattern?!) since it can't be 
// injected in this class
var fileInfoWrap = ObjectFactory.GetInstance<IFileInfoWrap>(
    new ExplicitArguments(new Dictionary<string, object>
    {
        {"fileName", filename}
    }));

Console.WriteLine("File exists? {0}",  fileInfoWrap.Exists); 

我不喜欢的是依赖项没有注入,ObjectFactory 不应该在这里(但我看不到其他创建它的方法)。 ExplicitArguments 让它变得混乱,并且参数名称是一个神奇的字符串。

对于我来说,要使其正常工作,StructureMap 配置类需要明确知道我想要使用哪个构造函数(我刚刚开始使用 StructureMap,所以这可能不是设置它的正确方法):

ObjectFactory.Initialize(x =>
{
    x.Scan(scan =>
    {
        scan.AssembliesFromPath(".");
        scan.RegisterConcreteTypesAgainstTheFirstInterface();
        scan.WithDefaultConventions();
    });

    // use the correct constructor (string instead of FileInfo)
    x.SelectConstructor(() => new FileInfoWrap(null as string));

    // setting the value of the constructor
    x.For<IFileInfoWrap>()
        .Use<FileInfoWrap>()
        .Ctor<string>("fileName")
        .Is(@".");
});

有人找到了更好的解决方案来针对 System.IO 类创建可测试的代码吗? 我知道部分问题在于 System.IO 类的设计。

c# dependency-injection tdd inversion-of-control base-class-library
2个回答
5
投票

我非常成功地使用的一种方法是为

System.IO
和 FCL 的其他部分中找到的类型推出我自己的代理类型。例如。我想依赖
System.IO.File
。我创建了一个名为
System.IO.Proxies
的库,并添加了一个具体类型
File
和一个接口
IFile
。接口
IFile
公开了与我从
System.IO.File
需要的所有成员等效的成员,并且具体类型除了将方法调用转发到
System.IO.File
之外什么也不做,来实现这些成员。
System.IO.Proxies
被排除在单元测试和代码覆盖率之外。在我的消费程序集中,我仅依赖于
System.IO.Proxies
,具体来说,我仅依赖于
IFile
。这样我就可以轻松模拟这种依赖关系,并为我的消费程序集实现 100% 的代码覆盖率。

(请注意,这是我对上一个问题的更一般的答案的定制版本。)


0
投票

您还可以尝试使用库https://github.com/Tum4ik/stinim-gen。 它为指定类的静态成员生成一个接口和一个实现包装器。

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