C# 如何使用NUnit测试与数据库相关的代码?

C# 如何使用NUnit测试与数据库相关的代码?,c#,database,unit-testing,tdd,nunit,C#,Database,Unit Testing,Tdd,Nunit,我想用NUnit编写命中数据库的单元测试。我希望每个测试的数据库都处于一致的状态。我认为事务处理可以让我“撤销”每个测试,所以我四处搜索,找到了2004-05年间关于这个主题的几篇文章: 这些似乎解决了为NUnit实现自定义属性的问题,该属性构建了在每次测试执行后回滚DB操作的能力 那很好,但是 此功能是否存在于NUnit中的某个地方 这项技术在过去4年中有改进吗 这仍然是测试数据库相关代码的最佳方法吗 编辑:我并不是特别想测试我的DAL,我更想测试与数据库交互的代码片段。对于这些

我想用NUnit编写命中数据库的单元测试。我希望每个测试的数据库都处于一致的状态。我认为事务处理可以让我“撤销”每个测试,所以我四处搜索,找到了2004-05年间关于这个主题的几篇文章:

这些似乎解决了为NUnit实现自定义属性的问题,该属性构建了在每次测试执行后回滚DB操作的能力

那很好,但是

  • 此功能是否存在于NUnit中的某个地方
  • 这项技术在过去4年中有改进吗
  • 这仍然是测试数据库相关代码的最佳方法吗

  • 编辑:我并不是特别想测试我的DAL,我更想测试与数据库交互的代码片段。对于这些“无接触”且可重复的测试,如果我能在每次测试后重置数据库,那将是非常棒的


    此外,我想将其简化为一个现有的项目,目前还没有测试场所。由于这个原因,我实际上无法为每个测试从头开始编写数据库和数据脚本。

    我将这些集成测试称为集成测试,但没关系。我为这些测试所做的是在每次测试之前,让我在测试类中的设置方法清除所有感兴趣的表。我通常手工编写SQL来实现这一点,这样我就不会使用测试中的类


    一般来说,我的数据层依赖于ORM,因此我不会在那里编写太多的单元测试。我觉得没有必要对我没有编写的代码进行单元测试。对于我在层中添加的代码,我通常使用依赖项注入来抽象出与数据库的实际连接,以便在测试代码时,它不会触及实际数据库。结合模拟框架来实现最佳效果。

    我刚去了一个.NET用户组,演示者说他在测试设置和拆卸中使用了SQLlite,并使用了内存选项。他不得不对连接进行一点篡改,并明确地破坏连接,但每次都会给出一个干净的DB


    对于这种测试,我尝试了NDbUnit(与NUnit合作)。如果内存可用,则它是Java平台的DbUnit端口。它有很多巧妙的命令,用于你正试图做的事情。项目似乎已移至此处:

    (过去是在)

    该来源似乎可通过以下链接获得:
    NUnit现在有一个[Rollback]属性,但我更喜欢用另一种方式。我使用这个类。有几种方法可以使用它

    [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


    话虽如此,我确实认为命中数据库的测试并不是真正的单元测试。我仍然编写它们,但我认为它们是集成测试。我仍然认为它们提供了价值。我经常使用它们的一个地方是测试LINQtoSQL代码。我不使用设计师。我手写DTO和属性。大家都知道我弄错了。集成测试有助于发现我的错误。

    考虑创建一个数据库脚本,以便可以从NUnit自动运行它,也可以手动运行其他类型的测试。例如,如果使用Oracle,则从NUnit中启动SqlPlus并运行脚本。这些脚本通常写得更快,也更容易阅读。同样,非常重要的是,从Toad或类似工具运行SQL比从代码运行SQL或从代码执行ORM更具启发性。通常,我将创建一个setup和teardown脚本,并将它们放在setup和teardown方法中

    您是否应该从单元测试中检查DB是另一个讨论。我相信这样做通常是有道理的。对于许多应用程序来说,数据库是绝对的行动中心,逻辑高度基于集合,所有其他技术、语言和技术都在传递鬼影。随着函数式语言的兴起,我们开始意识到SQL和JavaScript一样,实际上是一种伟大的语言,这些年来一直在我们的眼皮底下


    顺便说一句,LINQtoSQL(我在概念上喜欢,但从未使用过)在我看来几乎像是一种从代码中执行原始SQL的方法,而不承认我们正在做什么。有些人喜欢SQL,知道自己喜欢它;有些人喜欢SQL,但不知道自己喜欢它。:)

    不幸的是,这种方法不适用于我的项目(数百个表、过程、gig数据)。这对于现有的项目来说是非常困难的,但是你的单元测试应该被分解成更小的,更集中的类,而不是所有的表。您只需要处理这个特定测试类涉及的表。此外,在现有项目上改装单元测试可能最好是在“按需”的基础上完成——比如当您需要重构或修复bug时。然后,您可以围绕现有代码编写一个“盒子”测试,以确保您的更改不会破坏任何东西(或修复bug)。我真的喜欢。另外,我不想为了让db进入“准备就绪”状态而编写大量的fixture代码。在使用这种方法两周后,我对它非常满意,再次感谢!我最终使用了一个非常类似的模式,但是使用了一个处理数据库琐事的基类,包括设置连接等等。唯一的问题是,如果您不提交,那么您就不能测试数据是否已提交到数据库?i、 我想要我的t