Unit testing 文件访问、单元测试、依赖注入
我最近问了一个问题,关于将业务逻辑与数据访问分离以使应用程序可测试()的最佳方法。感谢Jeff Sternal,我为数据访问创建了接口,并将其具体实现从应用程序顶层传递到了BL。但现在我正试图弄清楚如何将文件访问方法与业务逻辑分离Unit testing 文件访问、单元测试、依赖注入,unit-testing,dependency-injection,file-access,Unit Testing,Dependency Injection,File Access,我最近问了一个问题,关于将业务逻辑与数据访问分离以使应用程序可测试()的最佳方法。感谢Jeff Sternal,我为数据访问创建了接口,并将其具体实现从应用程序顶层传递到了BL。但现在我正试图弄清楚如何将文件访问方法与业务逻辑分离 假设我有一个函数,它将数据从数据库加载到数据集,格式化加载的数据(格式存储在某个外部xml文件中),最后将数据集序列化为文件。所以,为了支持可测试性,我需要将访问文件系统的所有函数移动到某个接口。但首先,我发现只调用dataset.WriteXml(文件)非常方便,但
假设我有一个函数,它将数据从数据库加载到数据集,格式化加载的数据(格式存储在某个外部xml文件中),最后将数据集序列化为文件。所以,为了支持可测试性,我需要将访问文件系统的所有函数移动到某个接口。但首先,我发现只调用dataset.WriteXml(文件)非常方便,但为了可测试性,我必须创建接口并将dataset.WriteXml()移动到它的实现中,这在我看来是不必要的层,并使代码不那么明显。第二,如果我将所有访问文件系统的方法移到一个接口上,这将违反SRP原则,因为序列化\反序列化数据集和从文件中读取数据格式似乎是不同的职责,对吗?从您的描述中不完全清楚您的代码在做什么。代码所做的最重要的事情是什么?是福马汀吗?我想说的是,不要费心测试xml的编写。无论如何,你将如何验证这一点
如果您必须读取和写入文件,那么最好修改代码,以便传入streamreader/streamwriter或textreader/textwriter,调用代码将注入一个类似memorystream的实例,用于测试,filestream用于生产中的实际i/o。您可以使用
WriteXml()的版本
它接受一个流
或文本编写器
,并设置您的代码,以便将此对象传递到您的代码中,然后用于测试模拟对象中的传递。我认为您需要进一步拆分代码
你说:
假设我有一个函数
- 网络服务
- 文件系统
- 数据库
- Com对象
要快速总结,如果您真的想使此可测试,我建议:
数据集
传递给此类DataSet
作为IXmlSerializable
你也可以嘲笑public class EmployeeService {
private IEmployeeRepository _Repository;
public EmployeeService(IRepository repository) {
this._Repository = repository;
}
public void ExportEmployeeData(int employeeId, string path) {
DataSet dataSet = this._Repository.Get(employeeId);
// ... Format data in the dataset here ...
dataSet.WriteXml(path);
}
}
此代码简单有效,但不可测试(无副作用)。此外,将所有这些逻辑放在一个地方违反了单一责任原则
根据您的需要,这可能很好,我们总是需要平衡可测试性和影响我们设计的其他因素
但是,如果您想进一步分离责任并使其可测试,可以通过将格式化逻辑移动到其自己的类中(将实现IEmployedatasetFormatter
),然后将IEmployedatasetFormatter
注入此方法调用来实现(或者,我们可以将其注入服务的构造函数中,就像IEEmployeeRepository
)。格式化数据的方法将返回一个IXmlSerializable
,因此我们可以模拟它进行安全、独立的测试:
public interface IEmployeeDataSetFormatter {
IXmlSerializable FormatForExport(DataSet dataSet);
}
public class EmployeeDataSetFormatter: IEmployeeDataSetFormatter {
public IXmlSerializable FormatForExport(DataSet dataSet) {
// ... Format data in the dataset here ...
return (IXmlSerializable) dataSet;
}
}
public void ExportEmployeeData2(int employeeId, string path, IEmployeeDataSetFormatter formatter) {
DataSet dataSet = this._Repository.Get(employeeId);
IXmlSerializable xmlSerializable = formatter.FormatForExport(dataSet);
// This is still an intermediary step - it's probably worth
// moving this logic into its own class so you don't have to deal
// with the concrete FileStream underlying the XmlWriter here
using (XmlWriter writer = XmlWriter.Create(path)) {
xmlSerializable.WriteXml(writer);
}
}
这有一定的成本。它增加了一个额外的接口、一个额外的类和一点复杂性。但它是可测试的,并且更模块化(我认为这是一个好方法)。使用额外的类。在测试它时,更容易:
- 对于数据集导出器/写入器-检查修改后的数据是否传递给写入器
- 对于格式化程序-检查格式化逻辑
- 使用dataSet.WriteXml,您只需在v.simple类中执行此操作…作为一个一行程序,您不需要为其添加一个集中的集成测试…但是如果存储上的u l8r是在一个外部系统中,您可能会选择在另一个实现中执行此操作,而不受影响