单元测试数据库库
单元测试数据库的简介以及所提供的库如何提供帮助。
引言
我非常喜欢单元测试;这是一个在许多地方都讨论过的实践,所以我不会深入讨论是否应用它的优点和缺点。
我也非常喜欢在进行单元测试时使用模拟。通过开发彼此松散耦合的对象并注入模拟,您可以测试系统的很大一部分,而无需依赖外部资源(如数据库)。然而,总会有我们确实想检查数据访问代码是否在数据库上正常工作的时候;这时事情就会变得棘手,而这正是本文和所提供的库的目的所在。
注:当前库使用 NUnit 作为单元测试框架;但是,将其更改为使用 MBUnit 等其他框架并不需要太多工作。
为什么针对数据库进行测试很困难?
我们在测试用例中无法完全控制数据库。如果我们测试的是我们的 BLL、域、服务层等,并且使用了数据访问代码的模拟,那么我们就可以完全控制返回什么。这提供了一组可重复且可预测的数据供测试。当我们针对数据库进行测试时,我们无法保证我们想要测试的数据没有发生变化——这可能是由于外部干预,或者如果我们不小心,我们其他的测试可能已经改变了数据。
我们可以采取哪些步骤来帮助针对数据库进行测试?
- 为单元测试使用单独的数据库。这可能是一个相当大的挑战,具体取决于您在数据库中脚本化对象有多严格(使用与其他环境相同的数据库会带来麻烦)。
- 为如何使用此数据库设定准则。确保此数据库与其他环境数据库不会过于不同步(数据库比较工具或数据库源代码控制可能会有所帮助)。
- 确保每个测试都以可预测的数据开始,并在下一个测试之前删除这些数据。
数据库单元测试库如何提供帮助
所提供的库提供了一个基类,您的测试夹具类可以继承它。它提供了以下功能:
- 它可以被告知在每次测试之前和之后通过 Setup 和 Teardown 运行 SQL 脚本。这有助于确保在测试前数据处于可预测状态,并有助于在下一个测试前清理数据。
- 它还封装了常用的数据库访问代码,这样您就不必一遍又一遍地手动创建连接代码。它还以一种通用的方式做到这一点,利用 ADO.NET 的
DbProviderFactory
,因此您应该能够通过微小的配置更改来使用它来处理不同的数据库供应商(我只在 SQL Server 2000 上进行过测试)。
使用库
- 从您要执行数据库单元测试的项目中,添加对 DatabaseUnitTestingLibrary 项目/DLL 的引用。
- 现在,只需创建一个测试夹具类并继承自
DatabaseTestFixture
类。 - 添加一个 app.config 文件。
- 添加一个新的
appSetting
,名为data-scripts-directory
,并将 SQL 脚本的存放位置填入其中。 - 添加一个连接字符串部分,并将其命名为
unit-testing-connection
。现在,填入connectionString
和providerName
属性。
运行设置和拆卸脚本
该库使用 data-scripts-directory
设置中指定的位置来查找脚本。该库使用以下约定:
- 设置脚本将以 .setup.sql 作为后缀
- 拆卸脚本将以 .teardown.sql 作为后缀
所以,如果我有 staff 的设置和拆卸脚本,它们将被命名为 staff.setup.sql 和 staff.teardown.sql。
一旦 SQL 脚本就位,您现在需要覆盖 SetupDataScriptFiles
和 TeardownDataScriptFiles
属性中的一个或两个,并提供一个不带后缀的脚本名称字符串数组。例如:
protected override string[] SetupDataScriptFiles
{
get { return new string[] { "staff" }; }
}
protected override string[] TeardownDataScriptFiles
{
get { return new string[] { "staff" }; }
}
将运行 staff.setup.sql 和 staff.teardown.sql 脚本。
在我的设置脚本中,我倾向于放置用于插入数据的 SQL,而在我的拆卸脚本中,我倾向于放置用于删除这些数据并截断表以重置种子列的 SQL。我认为在 95% 的情况下都会是这样。
执行常见数据访问
在测试中执行数据访问以验证数据库中是否存在正确数据时,DatabaseTestFixture
类提供了一个名为 DataAccess
的属性,该属性在底层暴露了一个名为 DataAccessHelper
的对象。该对象负责打开自己的连接,并实现 IDisposable
,它会告诉它何时处理其连接。其用法示例如下:
using (DataAccess)
{
IDataReader reader = DataAccess.ExecuteReader(sql);
while (reader.Read())
{
Assert.AreEqual("Bloggs", reader.GetString(1));
Assert.AreEqual("Joe", reader.GetString(2));
}
}
在使用 DataAccess
时,请确保使用 using
语句或 try...finally
块,并在 finally
块中调用 Dispose
,以确保连接被正确处理。
库的使用
此库不受任何许可限制,可自由用于商业和非商业软件。请随意对源代码进行任何修改。如果您确实进行了任何修改,请在此处发表评论,说明修改内容,或给我发送电子邮件,以便帮助 CodeProject 社区中的其他人!谢谢。
历史
- V1.0 - 2008/04/13 - 初稿。