C# 如何使用 NUnit 测试与数据库相关的代码?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/321180/
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
How do I test database-related code with NUnit?
提问by Michael Haren
I want to write unit tests with NUnit that hit the database. I'd like to have the database in a consistent state for each test. I thought transactions would allow me to "undo" each test so I searched around and found several articles from 2004-05 on the topic:
我想用 NUnit 编写访问数据库的单元测试。我希望每次测试的数据库都处于一致的状态。我认为事务可以让我“撤消”每个测试,所以我四处搜索并找到了 2004-05 年关于该主题的几篇文章:
- http://weblogs.asp.net/rosherove/archive/2004/07/12/180189.aspx
- http://weblogs.asp.net/rosherove/archive/2004/10/05/238201.aspx
- http://davidhayden.com/blog/dave/archive/2004/07/12/365.aspx
- http://haacked.com/archive/2005/12/28/11377.aspx
- http://weblogs.asp.net/rosherove/archive/2004/07/12/180189.aspx
- http://weblogs.asp.net/rosherove/archive/2004/10/05/238201.aspx
- http://davidhayden.com/blog/dave/archive/2004/07/12/365.aspx
- http://haacked.com/archive/2005/12/28/11377.aspx
These seem to resolve around implementing a custom attribute for NUnit which builds in the ability to rollback DB operations after each test executes.
这些似乎解决了为 NUnit 实现自定义属性的问题,该属性构建了在每次测试执行后回滚数据库操作的能力。
That's great but...
这很棒,但是...
- Does this functionality exists somewhere in NUnit natively?
- Has this technique been improved upon in the last 4 years?
- Is this still the best way to test database-related code?
- 这个功能是否存在于 NUnit 的某个地方?
- 在过去的 4 年中,这项技术是否得到了改进?
- 这仍然是测试数据库相关代码的最佳方式吗?
Edit: it's not that I want to test my DAL specifically, it's more that I want to test pieces of my code that interact with the database. For these tests to be "no-touch" and repeatable, it'd be awesome if I could reset the database after each one.
编辑:并不是我想专门测试我的 DAL,而是我想测试与数据库交互的代码片段。为了使这些测试“无接触”且可重复,如果我能在每次测试后重置数据库,那就太棒了。
Further, I want to ease this into an existing project that has no testing place at the moment. For that reason, I can't practically script up a database and data from scratch for each test.
此外,我想将其简化到目前没有测试位置的现有项目中。出于这个原因,我实际上无法为每个测试从头开始编写数据库和数据的脚本。
采纳答案by Mike Two
NUnit now has a [Rollback] attribute, but I prefer to do it a different way. I use the TransactionScopeclass. There are a couple of ways to use it.
NUnit 现在有一个 [Rollback] 属性,但我更喜欢用不同的方式来做。我使用TransactionScope类。有几种方法可以使用它。
[Test]
public void YourTest()
{
using (TransactionScope scope = new TransactionScope())
{
// your test code here
}
}
Since you didn't tell the TransactionScope to commit it will rollback automatically. It works even if an assertion fails or some other exception is thrown.
由于您没有告诉 TransactionScope 提交它会自动回滚。即使断言失败或抛出其他一些异常,它也能工作。
The other way is to use the [SetUp] to create the TransactionScope and [TearDown] to call Dispose on it. It cuts out some code duplication, but accomplishes the same thing.
另一种方法是使用 [SetUp] 创建 TransactionScope 并使用 [TearDown] 对其调用 Dispose。它减少了一些代码重复,但完成了同样的事情。
[TestFixture]
public class YourFixture
{
private TransactionScope scope;
[SetUp]
public void SetUp()
{
scope = new TransactionScope();
}
[TearDown]
public void TearDown()
{
scope.Dispose();
}
[Test]
public void YourTest()
{
// your test code here
}
}
This is as safe as the using statement in an individual test because NUnit will guarantee that TearDown is called.
这与单个测试中的 using 语句一样安全,因为 NUnit 将保证调用 TearDown。
Having said all that I do think that tests that hit the database are not really unit tests. I still write them, but I think of them as integration tests. I still see them as providing value. One place I use them often is in testing LINQ to SQL code. I don't use the designer. I hand write the DTO's and attributes. I've been known to get it wrong. The integration tests help catch my mistake.
说了这么多,我确实认为命中数据库的测试并不是真正的单元测试。我仍然编写它们,但我认为它们是集成测试。我仍然认为它们提供了价值。我经常使用它们的一个地方是测试 LINQ to SQL 代码。我不使用设计器。我手写了 DTO 和属性。众所周知,我弄错了。集成测试有助于发现我的错误。
回答by tvanfosson
I would call these integration tests, but no matter. What I have done for such tests is have my setup methods in the test class clear all the tables of interest before each test. I generally hand write the SQL to do this so that I'm not using the classes under test.
我会称这些集成测试,但无所谓。我为此类测试所做的是让我在测试类中的设置方法在每次测试之前清除所有感兴趣的表。我通常会手动编写 SQL 来执行此操作,这样我就不会使用被测类。
Generally, I rely on an ORM for my datalayer and thus I don't write unit tests for much there. I don't feel a need to unit test code that I don't write. For code that I add in the layer, I generally use dependency injection to abstract out the actual connection to the database so that when I test my code, it doesn't touch the actual database. Do this in conjunction with a mocking framework for best results.
通常,我的数据层依赖于 ORM,因此我不会在那里编写太多单元测试。我觉得没有必要对我不写的代码进行单元测试。对于我在层中添加的代码,我通常使用依赖注入来抽象出与数据库的实际连接,这样当我测试我的代码时,它不会触及实际的数据库。结合模拟框架执行此操作以获得最佳结果。
回答by nportelli
I just went to a .NET user group and the presenter said he used SQLlite in test setup and teardown and used the in memory option. He had to fudge the connection a little and explicit destroy the connection, but it would give a clean DB every time.
我刚刚去了一个 .NET 用户组,演示者说他在测试设置和拆卸中使用了 SQLlite,并使用了内存选项。他不得不稍微捏造连接并明确破坏连接,但每次都会提供干净的数据库。
http://houseofbilz.com/archive/2008/11/14/update-for-the-activerecord-quotmockquot-framework.aspx
http://houseofbilz.com/archive/2008/11/14/update-for-the-activerecord-quotmockquot-framework.aspx
回答by Scott Lawrence
For this sort of testing, I experimented with NDbUnit (working in concert with NUnit). If memory serves, it was a port of DbUnit from the Java platform. It had a lot of slick commands for just the sort of thing you're trying to do. The project appears to have moved here:
对于此类测试,我尝试使用 NDbUnit(与 NUnit 协同工作)。如果没记错的话,它是从 Java 平台移植的 DbUnit。它有很多巧妙的命令,可以用于您正在尝试做的事情。该项目似乎已移至此处:
http://code.google.com/p/ndbunit/
http://code.google.com/p/ndbunit/
(it used to be at http://ndbunit.org).
(它曾经位于http://ndbunit.org)。
The source appears to be available via this link: http://ndbunit.googlecode.com/svn/trunk/
来源似乎可通过此链接获得:http: //ndbunit.googlecode.com/svn/trunk/
回答by Mike
Consider creating a database script so that you can run it automatically from NUnit as well as manually for other types of testing. For example, if using Oracle then kick off SqlPlus from within NUnit and run the scripts. These scripts are usually faster to write and easier to read. Also, very importantly, running SQL from Toad or equivalent is more illuminating than running SQL from code or going through an ORM from code. Generally I'll create both a setup and teardown script and put them in setup and teardown methods.
考虑创建一个数据库脚本,以便您可以从 NUnit 自动运行它,也可以为其他类型的测试手动运行它。例如,如果使用 Oracle,则从 NUnit 中启动 SqlPlus 并运行脚本。这些脚本通常编写速度更快且更易于阅读。此外,非常重要的是,从 Toad 或等效程序运行 SQL 比从代码运行 SQL 或从代码通过 ORM 更有启发性。通常我会创建一个安装和拆卸脚本,并将它们放在安装和拆卸方法中。
Whether you should be going through the DB at all from unit tests is another discussion. I believe it often does make sense to do so. For many apps the database is the absolute center of action, the logic is highly set based, and all the other technologies and languages and techniques are passing ghosts. And with the rise of functional languages we are starting to realize that SQL, like JavaScript, is actually a great language that was right there under our noses all these years.
您是否应该从单元测试中完全通过数据库是另一个讨论。我相信这样做通常是有意义的。对于许多应用程序来说,数据库是绝对的操作中心,逻辑是高度设置的,所有其他技术、语言和技术都在传递鬼魂。随着函数式语言的兴起,我们开始意识到 SQL 和 JavaScript 一样,实际上是多年来一直在我们眼皮底下的伟大语言。
Just as an aside, Linq to SQL (which I like in concept though have never used) almost seems to me like a way to do raw SQL from within code without admitting what we are doing. Some people like SQL and know they like it, others like it and don't know they like it. :)
顺便说一句,Linq to SQL(我在概念上喜欢,但从未使用过)在我看来几乎像是一种从代码中执行原始 SQL 而不承认我们在做什么的方法。有些人喜欢 SQL 并且知道他们喜欢它,有些人喜欢它但不知道他们喜欢它。:)