我正在创建一个CSV阅读器(是的,我知道Fast CSV Reader和FileHelpers)。 CsvReader类使用CsvParser类来解析CSV文件。我想让CsvReader类单元可测试,所以我希望能够在外部设置所使用的CsvParser类(同样,您也可以创建自己的实现)。我也不想创建一个解析器并在正常使用时传递它。
这就是我想要的方式。
var reader = new CsvReader( "path/to/file.csv" );
执行此操作时,我可以在CsvReader的构造函数中创建CsvParser,并具有更改解析器的属性。
public ICsvParser Parser { get; set; }
public CsvReader( filePath )
{
Parser = new CsvParser( filepath );
}
但是在单元测试时,总是创建默认解析器,我只想测试CsvReader。
解析器可以传递给构造函数,但我不想在正常使用时单独创建解析器。这似乎是一个工厂的好地方。
这似乎是使用IOC时的常见问题。对此有什么好的解决方案?
答案 0 :(得分:5)
解决方案是重写CsvReader
的构造函数以接受ICsvParser
的实现,并且ICsvParser
的具体实现应该有一个构造函数接受其依赖项(文件的路径)解析)并且应该将已构造的ICsvParser
注入到CsvReader
的构造函数中:
public CsvReader(ICsvParser parser) {
this.Parser = parser;
}
ICsvParser
应该已经被构造为接受它的依赖性(要解析的文件的路径)。
所以:
// path is string containing path to file to parse
ICsvParser parser = new SomeCsvParser(path);
ICsvReader reader = new CsvReader(parser);
关键是CsvReader
不需要路径,只需要CsvParser
。此外,CsvReader
不需要知道CsvParser
的依赖关系(它需要一个文件的路径来解析),以免它依赖于这些依赖关系。
new
是一种气味。
答案 1 :(得分:3)
如果这是IoC,你将传递CsvParser而不是路径。
答案 2 :(得分:0)
创建两个构造函数,一个使用解析器接口作为参数,另一个只使用filePath
。让第二个创建CsvParser
并让它用这个对象调用第一个。然后,您的测试代码可以使用第一个构造函数并传递模拟CsvParser。
此解决方案有一个缺点:包含CsvReader的程序集必须引用包含CsvParser的程序集。在你的情况下,你必须自己决定是否可以。
答案 3 :(得分:0)
通常你会通过构造函数传入解析器并使用IoC容器创建你的对象,然后这会将解析器实例注入到构造函数中。
在单元测试中,您可以直接传入模拟解析器,例如新的CsvReader(新的MockParser())或者为你的IoC配置一个为你注入模拟的测试配置。