单元测试数据访问层的方法

Posted

技术标签:

【中文标题】单元测试数据访问层的方法【英文标题】:Ways of unit testing data access layer 【发布时间】:2013-02-06 16:43:59 【问题描述】:

我一直在尝试寻找一种有效的方法来用 C# 对我的数据访问层进行单元测试。我是一名 Java 开发人员,只使用了大约 6 个月的 C#,过去我使用一个名为 DBUnit 的库来针对已知状态数据库进行测试。我还没有找到可以使用的类似活动库,最接近的似乎是 nDBUnit,但它已经有一段时间没有活动了。

似乎在 C# 中如何以及为什么有很多相互矛盾的方法。理想情况下,我想使用模拟测试数据访问层,而无需连接到数据库,然后在单独的一组测试中对存储过程进行单元测试。

在我正在开发的系统中,数据访问层是使用 ADO.net(不使用实体框架)来调用 SQL Server 上的存储过程。

以下是我必须使用的示例代码;要沿着模拟路径走下去,我必须能够模拟 SqlCommand(使用 IDbCommand)和/或模拟 SqlConnection。

所以我的问题是什么似乎是最好的方法(如果有这样的事情)来做到这一点?到目前为止,唯一的方法是制作传递给构造函数的 Proxy 对象,以便它可以返回模拟的 Sql* 对象进行测试。

我还没有机会查看所有可用的 C# 模拟库。

public class CustomerRepository : ICustomerRepository

   private string connectionString;

   public CustomerRepository (string connectionString)
   
     this.connectionString = connectionString;
   

   public int Create(Customer customer)
   

     SqlParameter paramOutId = new SqlParameter("@out_id", SqlDbType.Int);
     paramOutId.Direction = ParameterDirection.Output;
     List<SqlParameter> sqlParams = new List<SqlParameter>()
     
       paramOutId,
       new SqlParameter("@name", customer.Name)
     

     SqlConnection connection = GetConnection();
     try
     
       SqlCommand command = new SqlCommand("store_proc_name", connection);

       command.CommandType = CommandType.StoredProcedure;

       command.Parameters.AddRange(sqlParams.ToArray());

       int results = command.ExecuteNonQuery();

       return (int) paramOutId.Value;
     
     finally
     
       CloseConnection(connection);
     

   


【问题讨论】:

【参考方案1】:

很遗憾,您找不到将数据库置于已知状态并允许您针对数据库运行 CustomerRepository 以测试 CustomerRepository 的工具。但是,答案不是开始使用模拟来模拟所有的 ADO 调用。通过这样做,您最终会创建一个不真正测试任何逻辑的单元测试:它只是测试代码是否按照您认为应该编写的方式编写。

假设我最终编写了一个 SQL INSERT 作为在 SQL 数据库中创建客户的命令。现在假设我们正在进行更改,以便客户表具有不同的字段(这会破坏我们的 INSERT 命令),现在我们应该使用存储过程来创建客户。使用模拟的测试仍然可以通过,即使它正在测试的实现现在已经被破坏了。此外,如果您将实现修复为使用存储过程,您的单元测试现在将失败。如果单元测试在应该失败时继续通过,但在您修复系统时会失败,那么单元测试的意义何在?

请参阅this question 了解一些可能的替代方案。看起来标记的答案实际上只是最终使用 IKVM 在 C# 中使用 DBUnit。

因此,可能还有其他途径可以继续探索,但模拟 ADO 调用只会导致脆弱的测试,而这些测试并不能真正测试任何重要的东西。

【讨论】:

谢谢,我在周末给了这个更好的外观,我同意测试应该访问一个真实的数据库,对于我们的 Java 项目,它的工作方式就像这样,特别是表和列名已经更改为项目增长了。我看了一下 IKM 方法,我不想介绍太复杂的东西,其他开发人员无法管理和理解。 没错,这就是您应该创建使用存储库/DAL 的服务的原因。您模拟存储库,将其注入服务并测试该服务。然后,您将单独测试服务中的逻辑。如果存储库中断(返回不正确的数据),逻辑将失败,测试将失败。看到人们嘲笑存储库然后断言从该存储库返回总是很有趣。你没有用它来测试任何东西。【参考方案2】:

这一层的工作是将代码连接到数据库。它必须封装有关数据库连接和语法的知识。通常将领域语言映射到数据库语言。我将单元测试的这一部分视为集成测试,因此我测试数据库模式是否与真实数据库或测试数据库等效。有关该主题的更多信息here。

【讨论】:

谢谢,我觉得我看得太多了,我本来想这样做,但由于 c# 中缺乏这样的库,我开始寻找替代方案。【参考方案3】:

要测试 DataAccess Layer,您需要一个更复杂的结构。

DataAccess Layer 将从 Repository 对象调用引用。 Repo 对象将通过 UnitOfWork 设计模式从 Entity Framework DbSets 调用引用。

数据访问层 (TOP) | 工作单位 | 存储库模式类 | 英孚上下文 | 实际数据库

设置结构后,您将模拟存储库类。例如,项目将被插入到数据库中,而不是进入模拟对象。稍后您将对模拟对象进行断言以查看是否插入了项目。

请看Implementing the Repository and Unit of Work Patterns

【讨论】:

以上是关于单元测试数据访问层的方法的主要内容,如果未能解决你的问题,请参考以下文章

单元测试数据访问层 - 测试更新方法?

集成测试框架

如何让单元测试访问核心数据模型

我应该对数据访问层进行单元测试吗?这是一个好习惯以及如何去做?

领域驱动单元测试详解

将 Hasura 用作数据访问层的最佳和正确方法是啥