三颗纽扣

世界上最宽广的是海洋,比海洋更宽广的是天空,比天空更宽广的是人的胸怀

导航

数据库单元测试

单元测试本身就是一个不小的话题,就算积累了一些单元测试最佳实践,要进行数据库相关的单元测试也一直不是一件容易的事情。

单元测试的原则之一是上下文无关和可重复性,而数据库相关的测试则往往依赖数据库的状态,因此,如果不能在每个测试用例开始时 保证数据库处于一个已知的状态,那么单元测试的可重复性就很难保证,一个测试用例如果失败了,你如何断言是代码的问题,还是因为数据问题导致的呢?

反模式:在共享数据库环境下进行单元测试

这是最糟糕的情况之一,一些项目组的成员可能共同使用一个开发用的数据库,有时候一些测试就直接在这个开发数据库上运行了。 共享数据库环境完全得不到保证,一些测试也无法在这种环境下运行,想想看,你敢在共享数据库下执行 delete * 或者 Drop Table 这样的操作么?

这种单元测试基本完全不具备可重复性,每一次运行,都必须人工再进行一次确认才可保证测试结果是正确的。

模式:使用本机数据库进行单元测试

使用本机数据库进行单元测试是比较简单和安全的作法,毕竟本机数据库在每个人自己的机器上,不管如何折腾它,也不管把它折腾成了什么样, 都不会发生什么严重的后果,最大不了的,你可能因此遗失了本机的所有数据,需要从别人那里再要一份拷贝。

本机数据库有很多种,常见的包括 SqlExpress, SqlServerCe, MySql, SQLite 等,相对来说,SqlServerCe 以及 SQLite 这种纯文件型数据库要更有优势。 通常文件型数据库会自动创建数据文件,因此,测试运行者不需要手工准备测试环境。而且,测试完成后,可以方便的将测试数据库文件删除。

最佳实践:使用纯内存数据库或虚拟数据库进行单元测试

使用纯内存数据库作为单元测试应该是最佳选择,这意味着单元测试完全满足这样几个原则:

  • 它是完全上下文无关的,这个测试代码在任何人的机器上都可以运行,他不需要为了运行单元测试就安装一个 SqlExpress 或者在 SqlExpress 中创建一个测试用的数据库。
  • 它比较清洁,测试运行完成,一切烟消云散,不会留下任何垃圾文件。
  • 它的数据环境是明确的,内存数据库在单元测试开始时,通常是全空的,因此,你很确定单元测试时数据库状态是什么样的,这是一个好习惯,你必须在每次

单元测试的开始阶段创建表,插入初始数据,这样可保证对每个单元测试而言,数据库的状态时完全确定的。

当然,这同时需要一些额外的工具的支持,否则,每次测试前要写一大堆 create table SQL 语句,会疯掉的

  • 自动部署,能够简单的将数据库结构创建起来,以避免要写一大堆的 create table 。。。
  • 数据准备,应该能把 csv 或者 xml 等简单文本格式的数据简单的导入到库表中去,以避免要写一大堆 insert into .....
  • 数据断言,能够简单将数据表数据和 csv 等文本格式的数据进行比较以方便的验证数据库状态

最佳实践:将测试数据写在代码中

不论使用文件数据库还是内存数据库,通常都需要一些测试数据,将这些测试数据写在代码中比写在独立的数据文件中要更有效

  • 这样测试数据和测试代码就将处在同一个地方,阅读测试代码的时候你很清楚对应的数据时什么,要修改也很方便
  • 测试用例一多了以后,数据文件也会很多,要维护这些数据文件是很痛苦的,例如如何命名,如何得知一个数据文件是否还是有用的
  • 对于一些测试框架(比如 MSTest)要找到这些文件可能会不那么容易,它们可能不在 ..\..\ 目录下了,而对于另外一些测试框架,则它恰好就在 ..\..\ 目录下
  • 如果一定要一个数据文件,那么用一个 Excel 文件管理这些数据是不错的选择,在 Excel 中编辑数据非常方便,要将文本拷贝到 IDE 中,也很容易。但仅仅限于用它来编辑数据,不应该直接将它用作数据文件。

最佳实践:DbUnit 工具类

前面提到要有额外工具的支持,DbUnit 工具就是一个不错的选择(在Java中叫 DbUnit,在 DotNet 中叫 NDbUnit),我们对 NDbUnit 进行了一个简单的封装,提供了更简单的使用方法

Prepare 方法

通常在数据准备阶段进行数据准备工作,调用 Prepare.Insert(string ...) 方法将数据插入到数据库。它需要一个字符串参数,这个参数是一个多表 csv 风格的数据文件,例如

product
id|name|description
1|pc|pc server
2|notebook|note book computer

customer
id|name|phone
1|jumbo|
2|shwen|
...

每个数据字符串中可包含多个表的数据,每个表的第一行为表名,然后是字段名,后面跟随该表的数据行,直到一个空行表示该表结束。除了竖线,也可以用制表符 \t 作为分隔符, 一般来说手工键入数据的话,用竖线方便(因为你无法在IDE中输入制表符,IDE会自动将它转换为空格),而从Excel拷贝来的数据,则自动是制表符分割的。

当然,Prepare 也提供了数据准备的另外一些方法

  • Clear 清除指定的表或整个数据库的数据
  • Delete 仅删除指定的记录
  • Update 更新指定记录

Assert 方法

Assert 方法通常用在验证阶段对数据库内容进行断言

  • IsEmpty, IsNotEmpty 方法断言表为空或者不为空
  • EqualTo(string data) 断言数据库状态和指定的数据相同,这里 data 参数即前面提到的字符串格式的数据表。
  • Contains, NotContains 断言库表包含或者不包含指定的记录

posted on 2012-03-06 19:27  三颗纽扣  阅读(1536)  评论(0编辑  收藏  举报