C# 数据访问层单元测试方法

C# 数据访问层单元测试方法,c#,unit-testing,testing,persistence,data-access-layer,C#,Unit Testing,Testing,Persistence,Data Access Layer,我一直在寻找一种有效的方法,用C#对我的数据访问层进行单元测试。我主要是一名Java开发人员,只使用C#大约6个月,过去我使用了一个名为DBUnit的库来测试已知的状态数据库。我还没有找到一个类似的活动库可以使用,最近的似乎是nDBUnit,但它已经有一段时间没有活动了 在C#中,似乎有很多相互矛盾的方法来解释如何和为什么使用C#。理想情况下,我希望使用模拟来测试数据访问层,而不需要连接到数据库,然后在一组单独的测试中对存储过程进行单元测试 在我正在研究的系统中,数据访问层使用ADO.net(不

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

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

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

下面是我必须使用的示例代码;要继续模拟路径,我必须能够模拟SqlCommand(使用IDbCommand)和/或模拟SqlConnection

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

我还没有机会查看所有可用的C#mock库

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);
     }

   }

}
公共类CustomerRepository:ICCustomerRepository
{
私有字符串连接字符串;
公共CustomerRepository(字符串连接字符串)
{
this.connectionString=connectionString;
}
公共int创建(客户)
{
SqlParameter paramOutId=新的SqlParameter(“@out\u id”,SqlDbType.Int);
paramOutId.Direction=ParameterDirection.Output;
List sqlParams=新列表()
{
paramOutId,
新的SqlParameter(“@name”,customer.name)
}
SqlConnection=GetConnection();
尝试
{
SqlCommand=newsqlcommand(“store\u proc\u name”,connection);
command.CommandType=CommandType.storedProcess;
AddRange(sqlParams.ToArray());
int results=command.ExecuteNonQuery();
返回(int)paramOutId.Value;
}
最后
{
闭合连接(连接);
}
}
}

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

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

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

请参阅以了解一些可能的替代方案。看起来有标记的答案是,实际上只是在C#中使用DBUnit,然后使用IKVM


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

要测试DataAccess层,您需要更复杂的结构

DataAccess层将调用来自存储库对象的引用。 Repo对象将通过UnitOfWork设计模式从实体框架数据库集调用引用

数据访问层(顶部)
|
工作单元
|
存储库模式类
|
EF上下文
|
实际数据库

设置结构后,将模拟存储库类。例如,项将被插入到DB中,而将转到模拟对象。稍后,您将针对模拟对象断言是否插入了项


请看一看

谢谢,我在周末对此进行了更好的研究,我同意测试应该访问一个真实的数据库,我们的Java项目就是这样工作的,尤其是随着项目的增长,表和列名发生了变化。我看了一下IKM方法,我不想引入太复杂的东西,让其他开发人员无法管理和理解。这就是为什么您应该创建一个使用存储库/DAL的服务。您模拟存储库,将其注入服务并测试该服务。然后,您将单独测试服务中的逻辑。如果存储库中断(返回不正确的数据),逻辑将失败,测试将失败。看到人们嘲笑一个存储库,然后断言从该存储库返回总是很有趣的。谢谢,我想我对这件事看得太多了,我本来想这么做,但由于c#中缺少这样的库,我开始寻找替代方案。