我想用NUnit编写单元测试,这些测试命中了数据库。我希望每个测试都使数据库处于一致状态。我认为交易可以让我“撤销”每个测试,所以我四处搜索,发现2004-05上有几篇关于这个主题的文章:
这些似乎解决了为NUnit实现自定义属性的问题,该属性构建为在每次测试执行后回滚数据库操作的能力。
那很好但是......
编辑:这不是我想要专门测试我的DAL,而是我想要测试与数据库交互的代码片段。为了使这些测试“无法触摸”并且可重复,如果我可以在每个测试之后重置数据库,那就太棒了。
此外,我想将其简化为现在没有测试位置的现有项目。出于这个原因,我无法从头开始为每个测试编写数据库和数据。
答案 0 :(得分:72)
NUnit现在有[Rollback]属性,但我更喜欢以不同的方式。我使用TransactionScope类。有几种方法可以使用它。
[Test]
public void YourTest()
{
using (TransactionScope scope = new TransactionScope())
{
// your test code here
}
}
由于您没有告诉TransactionScope提交它,它将自动回滚。即使断言失败或抛出其他异常,它仍然有效。
另一种方法是使用[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
}
}
这与单个测试中的using语句一样安全,因为NUnit将保证调用TearDown。
说完所有我确实认为打到数据库的测试不是真正的单元测试。我仍然写它们,但我认为它们是集成测试。我仍然认为它们提供了价值。我经常使用它的一个地方是测试LINQ to SQL代码。我不使用设计师。我亲自写下DTO和属性。我知道我弄错了。集成测试有助于解决我的错误。
答案 1 :(得分:4)
我刚刚去了一个.NET用户组,演示者说他在测试设置和拆解中使用了SQLlite并使用了内存选项。他必须稍微捏造连接并明确地破坏连接,但每次都会给出一个干净的数据库。
http://houseofbilz.com/archive/2008/11/14/update-for-the-activerecord-quotmockquot-framework.aspx
答案 2 :(得分:2)
我会称这些集成测试,但无论如何。我为这些测试所做的是让测试类中的设置方法在每次测试之前清除所有感兴趣的表。我通常手写SQL来做这个,所以我没有使用被测试的类。
一般来说,我依赖于我的数据层的ORM,因此我不会在那里编写单元测试。我觉得不需要单独测试我不写的代码。对于我在图层中添加的代码,我通常使用依赖注入来抽象出与数据库的实际连接,这样当我测试我的代码时,它不会触及实际的数据库。与最佳结果的模拟框架结合使用。
答案 3 :(得分:1)
对于这种测试,我尝试了NDbUnit(与NUnit协同工作)。如果内存服务,它是来自Java平台的DbUnit的一个端口。它有很多光滑的命令,只是你想要做的事情。该项目似乎已移至此处:
http://code.google.com/p/ndbunit/
(以前是http://ndbunit.org)。
该来源似乎可通过此链接获得: http://ndbunit.googlecode.com/svn/trunk/
答案 4 :(得分:0)
考虑创建一个数据库脚本,以便您可以从NUnit自动运行它,也可以手动运行其他类型的测试。例如,如果使用Oracle,则从NUnit中启动SqlPlus并运行脚本。这些脚本通常编写起来更快,更易于阅读。此外,非常重要的是,从Toad运行SQL或等效运行比从代码运行SQL或从代码运行ORM更有启发性。通常我会创建一个设置和拆卸脚本,并将它们放入设置和拆解方法中。
是否应该通过单元测试来完成数据库是另一个讨论。我相信这样做通常是有意义的。对于许多应用程序而言,数据库是绝对的行动中心,逻辑基于高度设置,所有其他技术,语言和技术都在传递鬼魂。随着函数式语言的兴起,我们开始意识到SQL,就像JavaScript一样,实际上是一种很好的语言,这些年来我们的鼻子就在那里。
除此之外,Linq to SQL(我喜欢它在概念中虽然从未使用过)在我看来似乎是一种从代码中执行原始SQL而不承认我们正在做什么的方法。有些人喜欢SQL并且知道他们喜欢它,其他人喜欢它,并且不知道他们喜欢它。 :)