单元测试数据库

时间:2008-08-22 01:35:34

标签: database unit-testing transactions xtunit

去年夏天,我开发了一个基本的ASP.NET / SQL Server CRUD应用程序,单元测试是其中一项要求。当我尝试对数据库进行测试时遇到了一些麻烦。根据我的理解,单元测试应该是:

  • 无国籍
  • 彼此独立
  • 可重复且具有相同的结果,即没有持续的变化

在开发数据库时,这些要求似乎彼此不一致。例如,我无法在不确定要插入的行的情况下测试Insert(),因此我需要先调用Delete()。但是,如果他们还没有呢?然后我需要先调用Exists()函数。

我的最终解决方案涉及非常大的设置功能(哎呀!)和一个空的测试用例,它将先运行并指示设置运行没有问题。这是牺牲了测试的独立性,同时保持了他们的无国籍状态。

我发现的另一个解决方案是将函数调用包装在一个易于回滚的事务中,如Roy Osherove's XtUnit。这项工作,但它涉及另一个库,另一个依赖,对于手头的问题,它似乎有点过于沉重。

那么,面对这种情况,SO社区做了什么?


tgmdbm说:

  

您通常使用自己喜欢的   自动化单元测试框架   执行集成测试,这是   为什么有些人会感到困惑,但他们   不遵循相同的规则。你是   允许涉及混凝土   你的许多课程的实施   (因为他们已经过单元测试)。   您正在测试您的具体方式   课程彼此互动   与数据库

因此,如果我正确阅读,那么无法有效地对数据访问层进行单元测试。或者,数据访问层的“单元测试”是否涉及测试,例如,由类生成的SQL /命令,而不依赖于与数据库的实际交互?

9 个答案:

答案 0 :(得分:25)

除了断言表存在,包含预期的列以及具有适当的约束之外,没有真正的单元测试数据库的方法。但这通常不值得做。

您通常不会单元测试数据库。您通常会将数据库纳入集成测试。

您通常使用自己喜欢的自动单元测试框架来执行集成测试,这就是为什么有些人会感到困惑,但他们不遵循相同的规则。您可以参与许多课程的具体实施(因为它们已经过单元测试)。您正在测试具体类如何与彼此以及与数据库交互。

答案 1 :(得分:11)

DBunit

您可以使用此工具在给定时间导出数据库的状态,然后在进行单元测试时,可以在测试开始时自动回滚到其先前的状态。我们经常在工作的地方使用它。

答案 2 :(得分:5)

单元测试中外部依赖关系的常用解决方案是使用模拟对象 - 也就是说,模仿您正在测试的真实对象的行为的库。这并不总是直截了当,有时需要一些聪明才智,但如果您不想“自己动手”,那么.Net有几个好的(免费软件)模拟库。立即浮现在脑海中:

Rhino Mocks是一个声誉良好的人。

NMock是另一个。

也有很多商业模拟库。编写好的单元测试的一部分实际上是为它们设计代码 - 例如,通过使用有意义的接口,这样你就可以通过实现其界面的“假”版本来“模拟”一个依赖对象。可预测的方式,用于测试目的。

在数据库模拟中,这意味着“模拟”您自己的数据库访问层,其中的对象返回组成的表,行或数据集对象,供您的单元测试处理。

在我工作的地方,我们通常从头开始制作自己的模拟库,但这并不意味着你必须这样做。

答案 3 :(得分:4)

是的,您应该重构代码以访问访问数据库的存储库和服务,然后您可以模拟或存根这些对象,以便测试中的对象永远不会触及数据库。这比存储数据库的状态并在每次测试后重置它快得多!

我强烈推荐Moq作为您的模拟框架。我用过Rhino Mocks和NMock。 Moq非常简单,解决了我对其他框架的所有问题。

答案 4 :(得分:2)

我遇到了同样的问题并得出了与其他答案者相同的基本结论:不要打扰单元测试实际的数据库通信层,但是如果你想对模型函数进行单元测试(以确保它们) “正确地提取数据,正确格式化等等”,使用某种虚拟数据源和设置测试来验证正在检索的数据。

我也发现单元测试的简单定义不适合许多Web开发活动。但是这个页面描述了一些更“先进”的单元测试模型,可能有助于激发一些在各种情况下应用单元测试的想法:

Unit Test Patterns

答案 5 :(得分:2)

我解释了我在这种情况下使用的一种技术here

基本思想是在DAL中运用每个方法 - 断言结果 - 当每个测试完成后,回滚以便数据库干净(没有垃圾/测试数据)。

您可能找不到“唯一”的唯一问题是我通常会进行整个CRUD测试(从单元测试角度来看并不纯粹),但是这个集成测试允许您查看CRUD +映射代码。这样一来,如果它打破了,你就会在启动应用程序之前知道(当我试图快速运行时为我节省了大量工作)

答案 6 :(得分:1)

您应该做的是从脚本生成的数据库的空白副本运行测试。您可以运行测试然后分析数据,以确保它在测试运行后具有应有的功能。然后你只需删除数据库,因为它是一次性的。这一切都可以自动化,并且可以被视为原子动作。

答案 7 :(得分:1)

测试数据层和数据库一起留下了一些惊喜 项目。但是对数据库进行测试有其问题,主要的问题是 你正在测试许多测试共享的状态。如果在数据库中插入一行 在一次测试中,下一次测试也可以看到该线 您需要的是一种回滚您对数据库所做更改的方法 TransactionScope类非常聪明,可以处理非常复杂的事务, 以及嵌套事务,其中您的代码在测试调用中自己提交 本地交易。 这是一段简单的代码,显示了添加回滚功能是多么容易 你的测试:

    [TestFixture]
    public class TrannsactionScopeTests
    {
        private TransactionScope trans = null;

        [SetUp]
        public void SetUp()
        {
            trans = new TransactionScope(TransactionScopeOption.Required);
        }

        [TearDown]
        public void TearDown()
        {
            trans.Dispose();
        }

        [Test]
        public void TestServicedSameTransaction()
        {
            MySimpleClass c = new MySimpleClass();
            long id = c.InsertCategoryStandard("whatever");
            long id2 = c.InsertCategoryStandard("whatever");
            Console.WriteLine("Got id of " + id);
            Console.WriteLine("Got id of " + id2);
            Assert.AreNotEqual(id, id2);
        }
    }

答案 8 :(得分:0)

如果您使用LINQ to SQL作为ORM,那么您可以即时生成数据库(前提是您有足够的访问权来自用于单元测试的帐户)。见http://www.aaron-powell.com/blog.aspx?id=1125