C# 单元测试数据访问层的方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15000908/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Ways of unit testing data access layer
提问by wenic
I have be trying to look for an effective way in unit testing my data access layer in C#. I'm primary a Java developer and have only used C# for about 6 months, in the past i've used a library called DBUnit to test against a known state database. I haven't been able to find a similar active library that can be used, closest seems to be nDBUnit but it hasn't been active for awhile now.
我一直在尝试寻找一种有效的方法来对 C# 中的数据访问层进行单元测试。我主要是一名 Java 开发人员,只使用 C# 大约 6 个月,过去我使用一个名为 DBUnit 的库来测试已知状态数据库。我一直找不到可以使用的类似的活动库,最接近的似乎是 nDBUnit,但它现在已经有一段时间没有活动了。
There seems to be a lot of conflicting methods on how and why in C#. Ideally I want to test the data access layer using mocking without the need to connect to a database and then unit test the store procedure in a separate set of tests.
关于 C# 中的方式和原因,似乎有很多相互矛盾的方法。理想情况下,我想使用模拟测试数据访问层,而无需连接到数据库,然后在一组单独的测试中对存储过程进行单元测试。
In the system I'm working on, the data access layer is to use ADO.net (without the use of the Entity Framework) to call store procedures on a SQL Server.
在我正在研究的系统中,数据访问层是使用 ADO.net(不使用实体框架)来调用 SQL Server 上的存储过程。
Below is a sample code of what I have to work with; to go down the mocking path, I would have to be able to mock the SqlCommand (using IDbCommand) and/or mock the SqlConnection.
以下是我必须使用的示例代码;要沿着模拟路径走下去,我必须能够模拟 SqlCommand(使用 IDbCommand)和/或模拟 SqlConnection。
So my question is what seems to be the best way (if there is such a thing) to do this? So far the only way would be to make Proxy object that is passed into the constructor so it can return the mocked Sql* objects for testing.
所以我的问题是什么似乎是最好的方法(如果有这样的事情)来做到这一点?到目前为止,唯一的方法是制作传递给构造函数的 Proxy 对象,以便它可以返回模拟的 Sql* 对象进行测试。
I haven't had a chance to look at all the available C# mock libraries available yet.
我还没有机会查看所有可用的 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);
}
}
}
回答by GalSegal
This layer's job is to connect the code to the database. It has to encapsulate the knowledge about db connection and syntax. In usually maps the domain language to database language. I look at this part of the unit tests as integration test, and as such I test that the database schema is equivalent against real or test database. More on the subject here.
该层的工作是将代码连接到数据库。它必须封装有关数据库连接和语法的知识。通常将领域语言映射到数据库语言。我将单元测试的这一部分视为集成测试,因此我测试数据库模式是否与真实或测试数据库等效。更多关于这里的主题。
回答by Mark Hildreth
It's unfortunate that you can't find a tool that puts your database into a known state and lets you run your CustomerRepository against the database to test the CustomerRepository. However, the answer is not to start using mocks to mock out all of the ADO calls. By doing that, you end up creating a unit test that doesn't really test any logic: it's just testing that the code is written the way you think it should be written.
不幸的是,您找不到一种工具可以将您的数据库置于已知状态并让您针对数据库运行 CustomerRepository 以测试 CustomerRepository。但是,答案不是开始使用模拟来模拟所有 ADO 调用。通过这样做,您最终会创建一个不真正测试任何逻辑的单元测试:它只是测试代码是否按照您认为应该编写的方式编写。
Let's say that I end up writing a SQL INSERT as my command to create the customer in the SQL database. Now let's say that we're making a change so that the customer table has different fields (that breaks our INSERT command) and that now we should be using a stored procedure to create the customer. The test with mocks would still pass, even though the implementation it's testing is now broken. Additionally, if you fixed the implementation to use stored procedures, your unit tests would now fail. What is the point of a unit test if it continues to pass when it should fail but then would fail when you fixed the system?
假设我最终编写了一个 SQL INSERT 作为在 SQL 数据库中创建客户的命令。现在假设我们正在进行更改,以便客户表具有不同的字段(这会破坏我们的 INSERT 命令),现在我们应该使用存储过程来创建客户。带有模拟的测试仍然会通过,即使它正在测试的实现现在已经被破坏了。此外,如果您修复了使用存储过程的实现,您的单元测试现在将失败。如果单元测试在应该失败时继续通过,但在修复系统时会失败,那么单元测试的意义何在?
See this questionfor some possible alternatives. It looks like the marked answer is to actually just end up using DBUnit in C# using IKVM.
有关一些可能的替代方案,请参阅此问题。看起来明显的答案是实际上最终只是使用 IKVM 在 C# 中使用 DBUnit。
So, there might be alternative avenues to continue to explore, but mocking the ADO calls is just going to lead to brittle tests that don't really test anything important.
因此,可能有其他途径可以继续探索,但模拟 ADO 调用只会导致脆弱的测试,而这些测试并没有真正测试任何重要的东西。
回答by hakan
To test DataAccess Layer you will need a more complex structure.
要测试数据访问层,您将需要更复杂的结构。
DataAccess Layer will call references from Repository objects. Repo object will call references from Entity Framework DbSets via UnitOfWork design pattern.
数据访问层将调用来自 Repository 对象的引用。Repo 对象将通过 UnitOfWork 设计模式调用来自实体框架 DbSet 的引用。
DataAccess Layer (TOP)
|
UnitOfWork
|
Repository Pattern Classes
|
EF Context
|
Actual Database
数据访问层 (TOP)
|
工作单位
|
存储库模式类
|
英孚语境
|
实际数据库
After you set structure, then you will mock repository classes. For example, items will be inserted to DB instead will go to mock object. Later you will assert against your mock object to see item inserted or not.
设置结构后,您将模拟存储库类。例如,项目将被插入到数据库而不是去模拟对象。稍后您将对您的模拟对象进行断言以查看是否插入了项目。
Please take a look at Implementing the Repository and Unit of Work Patterns
请查看实现存储库和工作单元模式