2008-08-22 204 views
32

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

  • 无国籍
  • 相互独立
  • 重复相同的结果,即没有更改保存

这些要求似乎是在每个赔率其他当为数据库开发时。例如,我不能测试Insert()而不确定要插入的行不存在,因此我需要先调用Delete()。但是,如果他们不在那里呢?然后我需要先调用Exists()函数。

我最终的解决方案涉及非常大的设置函数(yuck!)和一个空的测试用例,它将首先运行,并表明安装程序没有问题。这是牺牲了测试的独立性,同时保持了他们的无状态。

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

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


tgmdbm说:

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

所以,如果我读这正确的,实在是没有办法有效单元测试数据访问层。或者,数据访问层的“单元测试”是否涉及测试,比如由类生成的SQL /命令,而与数据库的实际交互无关?

回答

25

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

您通常不会单元测试数据库。您通常涉及集成测试中的数据库。

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

11

DBunit

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

5

单元测试中外部依赖的通常解决方案是使用模拟对象 - 也就是说,模仿您正在测试的真实对象的行为的库。这并不总是直截了当,有时需要一些独创性,但如果你不想“自己推出”,那么在.Net中有几个好的(免费软件)模拟库。两个马上想到:

Rhino Mocks是一个有很好的声誉。

NMock是另一个。

也有大量的商业模拟库可用。编写好的单元测试的一部分实际上就是为你的代码设计代码 - 例如,通过使用有意义的接口,以便通过实现它的接口的“虚假”版本来“模拟”依赖对象,该接口的行为却在可预测的方式,用于测试目的。

在数据库嘲讽中,这意味着“嘲笑”您自己的数据库访问层,使用返回组成表,行或数据集对象的对象来处理单元测试。

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

1

你应该做的是从脚本生成的数据库空白副本中运行测试。您可以运行测试,然后分析数据以确保它在测试运行后完全符合它的要求。那么你只需删除数据库,因为它是一次性的。这可以全部自动化,并且可以被认为是原子动作。

4

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

我强烈建议Moq作为你的模拟框架。我用过Rhino Mocks和NMock。 Moq非常简单,解决了我与其他框架所遇到的所有问题。

2

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

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

Unit Test Patterns

2

我解释说,我一直在使用这个特别的情况here技术。

其基本思想是在DAL中运用每种方法 - 声明结果 - 并且每次测试完成后,回滚数据库以保证数据库干净(无垃圾/测试数据)。

您可能找不到“唯一”的唯一问题是我通常会执行一个完整的CRUD测试(从单元测试的角度来看不是纯粹的),但这个集成测试允许您查看您的CRUD +映射代码。通过这种方式,如果发生故障,在启动应用程序之前您将知道(在我尝试快速运行时为我节省大量工作)

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); 
     } 
    }