2012-01-07 38 views
6

我想创建单元测试代码,模拟调用.Net System.IO类,所以我可以真正进行单元测试而不是依赖于文件系统。 我正在使用SystemWrapper类来环绕BCL类。如何使用.Net IO类创建可测试的代码?

我想获得一个简单的例子,看看是否存在一个文件。

我遇到的问题是,在类注射的依赖不起作用,因为实例的依赖(通过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类的设计中。

+3

SystemWrapper主要含有极其漏抽象。对Streams,TextWriter,TextReader等进行IO建模将变得简单和容易。这些类已经是抽象的,完全消除了对SystemWrapper的需求。 – 2012-01-07 10:38:52

+0

另一个投票流 – 2012-01-07 11:36:27

+0

我的发现与SystemWrapper是它似乎是一个很好的接口包装,但它仍然是一个死胡同,因为原始类的工作方式。 例如,返回一个FileInfo对象数组不能被正确模拟。滚动我自己更简化的包装,它不需要模仿现有的类,而更多的工作会导致一个可行的解决方案恕我直言。 – 2012-01-08 06:48:37

回答

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%的代码覆盖率。

(请注意,这是我more general answer上一个问题的定制版本。)

+0

是的,那也是我结束的方式。 可以将呼叫委托给例如System.IO.FileInfo到一个单独的类。 滚动你自己的代理确实需要为每个类创建代理,这可能是一些工作,当然它可以与来自BCL类的所需功能一起增长。 同样,当返回其他类型(FileInfo)时,应该在代理中创建所有必需属性(名称,全名,长度)。 有人会认为这个问题已经'解决'了,已经避免了每个人的代理。 – 2012-01-07 14:10:20

+0

相当成功地使用了相同的方法。通常你不需要像_File_这样的类的所有方法/属性/事件。对于少数你确实需要编写这样的包装很容易。 – 2012-01-07 18:09:33

相关问题