如何对 DAL 进行单元测试?
我的应用程序中有一个数据访问层,它包装了 ADO.NET 数据提供程序。 DAL 将数据提供者返回的数据转换为.NET 对象。我看过很多建议不要对 DAL 进行单元测试的帖子,但它让我担心其中可能会出现很多问题 - 有很多循环、转换和空检查。
我有一些关于使用 RhinoMocks 之类的东西创建模拟 DbProvider 的想法,但是我在每个测试中必须模拟的接口数量将是巨大的,而且我必须设置的期望数量将使测试变得非常困难阅读。似乎每个测试都会比它正在测试的代码更复杂 - 从单元测试的 3 个目标的角度来看,这将是一场灾难:
- 可读性
- 可维护性
- 可信赖性
我有一个想法,实现一个友好的 DbProviderFactory 来加载示例数据xml。我可以在测试中通过依赖注入将其插入。它应该使维护测试变得更加简单。一个简单的例子可能是:
[TestCase]
public void CanGetCustomer()
{
var expectedCommand = new XmlCommand("sp_get_customer");
expectedCommand.ExpectExecuteDataReader(
resultSet: @"<customer firstName=""Joe"" lastName=""Blogs"" ... />");
var factory = new XmlProviderFactory(expectedCommand);
var dal = new CustomerDal(factory);
Customer customer = dal.GetCustomer();
Assert.IsNotNull(customer, "The customer should never be null");
Assert.AreEqual(
"Joe", customer.FirstName,
"The customer had an unexpected FirstName.");
}
我认为这种方法 - 使用友好的 DbProvider - 可能会使测试 DAL 代码变得更容易。它将具有以下优点:
- 测试数据采用 xml 格式,并且可以与单元测试一起放置在源代码管理中。它可以位于外部文件中,也可以位于测试中。
- 我没有使用真正的数据库,因此这消除了状态问题。因此,我不必在每次测试之前将数据库置于已知状态。
- 我不必在每个测试中模拟所有 ADO.NET 接口。我将编写一组假实现,我可以在整个代码库中重复使用它们。
人们可以对这个想法提出一些批评吗?是否已经有一个类似的实现可供我使用?
谢谢
I have a Data Access Layer in my application which wraps an ADO.NET data provider. The DAL converts the data returned by the data provider into .NET objects. I've seen a lot of posts advising against unit testing the DAL, but it worries me that so much could go wrong in there - there's lots of looping and casting and null checks.
I had some thoughts about creating a mock DbProvider with something like RhinoMocks, but the number of interfaces I'd have to mock out in each test would be overwhelming, and the number of expectations I'd have to set would make the tests very hard to read. It seems that each test would be more complex than the code it was testing - which would be a disaster from the perspective of the 3 goals of unit testing:
- Readability
- Maintainability
- Trustworthiness
I had an idea to implement a friendly DbProviderFactory to load the sample data from xml. I could plug it in via Dependency Injection in the tests. It should make maintaining the tests much simpler. A trivial example might be:
[TestCase]
public void CanGetCustomer()
{
var expectedCommand = new XmlCommand("sp_get_customer");
expectedCommand.ExpectExecuteDataReader(
resultSet: @"<customer firstName=""Joe"" lastName=""Blogs"" ... />");
var factory = new XmlProviderFactory(expectedCommand);
var dal = new CustomerDal(factory);
Customer customer = dal.GetCustomer();
Assert.IsNotNull(customer, "The customer should never be null");
Assert.AreEqual(
"Joe", customer.FirstName,
"The customer had an unexpected FirstName.");
}
I think this approach - using a friendly DbProvider - might make it easier to test the DAL code. It would have the following advantages:
- The test data would be in xml, and could be placed in source control along with the unit tests. It could be in external files, or in-line in the tests.
- I'm not using a real database so this eliminates the statefulness problem. So I don't have to put a database into a known state prior to each test.
- I don't have to mock out all of the ADO.NET interfaces in each test. I'll code one set of fake implementations which I can re-use all over the codebase.
Could people provide some critique on this idea? Is there already a similar implementation that I could use for this?
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我对数据访问类(DAL、DAC、DAO、存储库等)的正确单元测试(不,不是集成测试)这一主题进行了很多哲学争论。有些人认为这是毫无意义的,因为你正在进行集成测试。我发现对这些经常被忽视的代码单元进行单元测试具有巨大的价值。首先,为了正确地对数据访问类进行单元测试,它必须具有正确的结构,并且必须在消费者可以交互的沙子上划出界限——想想接口。数据访问实现应该定义一个接口,该接口实现了消费应用程序代码仅依赖的接口。您选择的基础结构代码(ADO.NET、NHibernate、NDatabase 等)应该具有您的数据访问代码仅依赖的接口。有了这些基础设施接口(IDBConnection、ISession、IDatabase 等)可用并正确利用后,您就可以使用您选择的模拟工具在单元测试中模拟这些接口。这将为您提供更高质量的数据访问代码,该代码已经过单元测试(模拟基础设施接口)、集成测试(针对真实数据库),并且具有较低的网络耦合。
需要注意的是:在我看来,需要警惕的一种糟糕的代码味道是当数据访问相关的代码渗透到数据访问(或持久性)层时。例如,如果您看到连接、命令、会话等的使用高于数据访问类实现,则这违反了关注点分离。
I get into a lot of philosophical arguments about the topic of proper unit testing (no, not integration testing) of data access classes (DALs, DACs, DAOs, Repositories, etc.). Some argue it is pointless since you are doing integration testing. I find tremendous value in unit testing these often neglected units of code. First, in order to properly unit test a data access class, it must be structured correctly and it must draw lines in the sand to which consumers may interact – think interfaces. The data access implementation should have an interface defined that it implements which the consuming application code only has a dependency on. Your choice of infrastructure code (ADO.NET, NHibernate, NDatabase, etc.) should have interfaces which your data access code only has dependencies on. With these infrastructure interfaces (IDBConnection, ISession, IDatabase, etc.) available and leveraged correctly, you can then mock these interfaces in your unit tests using your mocking tool of choice. This leaves you with higher quality data access code which has been unit tested (mocking infrastructure interfaces), integration tested (against a REAL database), and has lower net coupling all around.
One note: In my opinion, a bad code smell to be wary of is when data access related code bleeds past the data access (or persistence) layer. For example, if you see connections, commands, sessions, etc. used higher that the data access class implementation, this reeks of a violation of Separation of Concerns.