Unit testing 数据访问、单元测试、依赖注入
我最近有一项任务,创建一个简单的实用程序,允许将数据从具有特殊格式的文件导入数据库。我用很少的类实现了控制台应用程序(程序类和业务逻辑类一起运行,业务逻辑类又和数据访问类一起运行)。一切正常,但现在我正在考虑创建一些单元测试和重构应用程序(我以前没有创建真正的单元测试,很久以前只是一堆集成测试,所以我相信这个应用程序是练习的完美领域) 所以,问题是:数据访问类是静态的,这不允许对其进行模拟,从而创建真正的单元测试。要解决这个问题,我需要创建一个接口并在数据访问类中实现它。此外,我还必须向业务逻辑类添加一个构造函数,该类将接受该接口类型的参数。这意味着我将在application Main()方法中创建数据访问类,有人告诉我这不是最好的方法(入口点应该知道一些数据访问的事情,这真的可以吗?如果链更长或者应该有几个链呢?)。我知道我可以使用一些IoC容器,但我认为这是一个太简单的应用程序,无法使用容器Unit testing 数据访问、单元测试、依赖注入,unit-testing,dependency-injection,data-access,Unit Testing,Dependency Injection,Data Access,我最近有一项任务,创建一个简单的实用程序,允许将数据从具有特殊格式的文件导入数据库。我用很少的类实现了控制台应用程序(程序类和业务逻辑类一起运行,业务逻辑类又和数据访问类一起运行)。一切正常,但现在我正在考虑创建一些单元测试和重构应用程序(我以前没有创建真正的单元测试,很久以前只是一堆集成测试,所以我相信这个应用程序是练习的完美领域) 所以,问题是:数据访问类是静态的,这不允许对其进行模拟,从而创建真正的单元测试。要解决这个问题,我需要创建一个接口并在数据访问类中实现它。此外,我还必须向业务逻辑
谢谢 这里有一个简单的解决方案:不要直接调用数据访问类,而是使用助手方法:
public void insert (...) {
DataAccess.insert (...);
}
现在您可以覆盖这些调用。我建议按如下方式拆分测试:
DataAccess
在获得正确参数时做正确的事情insert()
的参数。根本不要调用DataAccess
DataAccess
。后面的测试将运行得非常快,这将使测试特殊情况等变得非常容易
您也不需要一直从#1运行测试。仅当您在
DataAccess
中或在发布之前更改某些内容时。这将使测试变得高效和愉快。假设您使用的是LINQ to SQL,也许您可以使用存储库模式将DataContext包装到一个稍后可以模拟的接口中,从而使单元测试成为可能
互联网上有一些关于这个主题的文章,这里有一篇:
我需要创建一个接口并在数据访问类中实现它。此外,我将不得不添加一个构造函数
到将
接受该接口的参数
类型。所以这意味着我最终会
在中创建数据访问类
应用程序Main()方法和
有些事告诉我这不是最好的
方法(如果
入口点应该知道一些
数据访问的东西?如果链
要长得多或者应该有
几条链条?)
恰恰相反!至少从可测试性的角度来看,这是最好的方法
使您的业务逻辑层可测试的唯一方法是,完全按照您的设想将其与数据访问层隔离
您的顶级应用程序就是责任所在——它是唯一需要知道具体数据访问类是什么的组件
如果链长得多,或者有几个链,那没什么大不了的(尽管你可能想考虑如果它失控的话会崩溃一些应用层)。在<代码>模型视图演示者< /代码>应用程序<代码>视图>代码中考虑此潜在代码,其中<代码>演示者< /代码>依赖于<代码>客户服务>代码>,它依赖于<代码>存储库< /代码>和对<代码> ActudioService < /代码>的依赖性(这也依赖于<代码>存储库< /代码>):
最后,如果您不想使用依赖项注入容器,则无需使用依赖项注入容器(尽管其中一些容器非常轻量级)-手动依赖项注入可以很好地工作,直到您开始到处重复(或者发现您希望在运行时配置依赖项).现在,我正在使用类型化数据集和自定义方法从文件读取和写入文件。最初我没有使用实体框架或linq2sql,因为在我编写这个应用程序的时候,我没有太多的时间用我没有经验的技术进行实验。但这是个好主意,也许我会修改我的DAL。补充评论作为答案,并没有太多space@Dev-回答问题会很快让人困惑。最终答案将被重新排序,亚伦可能会改变他的名字,甚至可能会删除他的答案。我建议在评论中回答-如果您需要比分配的字符更多的空间,您可以将响应分散到两条评论中。或者,如果您认为原始问题有助于其他人回答您的问题,您可以编辑原始问题以包含一些信息。谢谢Jeff,将把“答案”移到评论中。@Aaron,谢谢您的快速回复。如果我理解正确的话,建议是使用虚拟方法的包装器包装数据访问类,这样我就可以覆盖它们进行模拟。这是一个有趣的想法,但似乎我需要“复制”所有数据访问方法只是为了可测试性,每次我需要向数据访问类添加新方法时,我都必须创建一个合适的包装器。如果我在BL类中创建一个将创建数据访问对象的虚拟方法,这可以吗?如果我在BL类中创建一个将创建数据访问对象的虚拟方法,这可以吗?类似这样的内容:受保护的虚拟IDataAccess CreateDataAccess(){return new DataAccess();}然后我将能够重写单个方法来模拟数据访问对象。这种方法的缺点是什么?+1:很好
public CustomerView() {
IRespository repository = new ConcreteRepository();
IAccountingService accountingService = new ConcreteAccountingService(repository);
ICustomerService customerService = new ConcreteCustomerService(accountingService, repository)
this._Presenter = new CustomerPresenter(customerService);
}