使用 SQL Server Data Tools (SSDT) 进行 SQL 单元测试






4.64/5 (5投票s)
使用 SQL Server 数据工具 (SSDT) 进行 SQL 单元测试
本文包含什么?
本文解释了如何使用 SQL 单元测试功能来使应用程序更加健壮。虽然我们可以使用 Microsoft 提供的示例 AdventureWorks
数据库,但它对本文来说太大了,我们将创建一个非常简单的数据库以避免混淆并涵盖所有主题。数据库脚本在需要的地方提供,只需复制并在你的 SQL Server 版本中执行。
本文不包含什么?
它不包含 Visual Studio 2015 或更早版本的实现步骤,尽管相同的步骤可能适用于 VS 2015 或更早版本。
什么是 SQL 单元测试?
SQL 单元测试是指对数据库的数据、架构、存储过程、函数、视图和触发器进行独立的单元测试。就像通过 NUnit 或 Visual Studio 测试用例的代码单元测试、通过 Coded UI 进行的 UI 测试一样,SQL 测试在使应用程序健壮且无错误方面也起着重要作用。
SQL 单元测试现在已在 VS 2015 和 VS 2017 的社区版中提供,这是使用它的另一个原因。以前,此功能仅在付费版 VS 中提供。自 VS 2010 professional + 版本发布以来一直可用。
软件要求
Visual Studio Community 2017、SQL Server 2012 和 SQL Server Data Tools (SSDT)。SSDT 是一个免费工具,无需提供任何登录凭据即可下载。
请注意,应安装与 Visual Studio 版本对应的 SSDT,即,如果你使用的是 VS 2015,请安装适用于 VS 2015 的 SSDT 版本。如果你使用的是 VS 2013,请安装支持 VS 2013 的 SSDT。已安装的旧版本 SSDT 将无法与新版本 Visual Studio 一起使用。
建议使用 SQL Server 2012+ 和 VS 2012+。
2. 创建数据库并添加表
创建一个名为 "DBUnitTesting
" 的数据库。然后,执行附加的脚本。如果你想使用不同的数据库名称,请修改脚本并将 "DBUnitTesting
" 替换为新名称。
以下是表的结构和关系的数据库图。创建了三个表:Company
、Department
和 SubDepartment
。在此,CompanyId
在 Department
表中用作外键引用,DepartmentId
在 SubDepartment
表中用作外键引用。SubDepartmentLog
表用于审计目的。审计表不应包含任何外键链接。
创建表脚本
使用附加的 "Scripts" zip 文件夹中的 "CreateTables.txt"。
3. 在 Visual Studio 2017 中创建项目
转到 文件-> 新建-> 项目-> SQL Server-> SQL Server 数据库项目。如果系统中正确安装了相应的 SSDT,则会显示此选项。输入任何名称,例如 "SQLUnitTestingDB
"。
右键单击并选择 "导入" -> 数据库。提供有效的凭据并单击 "开始"。
几秒钟后,架构将导入到代码中。单击 "完成"。
添加新项目 -> 类 (.NET Framework),将其命名为 "TestCases
"。添加以下引用
- 添加对之前创建的 "
SQLUnitTestingDB
" 项目的引用,因为它包含架构详细信息。 - 添加 Nuget 包 -
Microsoft.VisualStudio.QualityTools.UnitTestFramework.Updated
右键单击,添加新项,选择 SQL Server,然后选择 "SQL Server 单元测试"。由于我们将首先测试 Company
表,因此将其命名为 "CompanyUnitTest
"。
为什么我们创建了一个单独的类?
我们也可以在 "SQLUnitTestingDB
" 项目中添加 SQL Server 单元测试,但将单元测试用例与包含架构信息的项目分开是一个好习惯。这有助于在长期内更好地维护项目,因为添加了许多测试用例。
3. 创建第一个单元测试
转到 "CompanyUnitTest.cs" 并重命名
- "
SqlTest1
" 为 "RowCountCompany
" - "
SqlTest1Data
" 为 "RowCountCompanyData
"
正确的命名有助于更好地维护项目。 "RowCountCompany
" 将在测试资源管理器中显示。
双击 "CompanyUnitTest.cs",设计器将打开。设计器中显示三个选项:预测试、测试、后测试。
预测试:通常用于,但不限于,在实际测试执行之前设置某些必需的条件。
测试: 主要命令或操作。
后测试: 通常用于,但不限于,如果在执行操作后需要清理表数据。
并非必须使用所有三个选项。现在,我们将使用测试选项。双击 "CompanyUnitTest
",设计器将打开。在左上方,可以看到已修改的测试用例名称 "RowCountCompany
"。
现在,请考虑,根据业务规则,数据库应有四个公司。请注意,单元测试不应根据数据库中存在的实际数据编写,而应根据业务需求规范文档中指定的业务规则编写。这一点非常非常重要,否则将无法实现编写单元测试用例的目标。我们的目标是将业务需求文档与数据库中存在的实际数据进行比较,并衡量偏差。这是开发人员在首次开始编写单元测试用例时常犯的错误,所以要避免。
单击 "click here to create" 链接并添加 SQL 查询。删除 "Data Checksum" 条件,然后使用 "+" 符号添加标量值。添加后,属性框将打开,在 "Expected Value" 列中输入 4。
保存并从 测试->窗口->测试资源管理器 打开测试资源管理器。右键单击并单击 "运行选定测试"。由于数据库中的数据正确,它将通过。
现在,假设数据库中的数据不正确。数据库中添加了更多公司,但业务需求规范文档中未指定。
再次在测试资源管理器中运行测试。它将失败并说明原因。这样我们就知道我们的数据不正确,必须修复。
4. 单元测试选项
SQL 单元测试提供多个选项,例如
- 数据校验和:这确保表中的数据未更改。
在属性选项卡中单击 "Press to configure"。单击 "Select Connection" 并选择表。单击 "Retrieve",数据将显示。单击 "OK"。
在测试资源管理器中运行测试用例。它将通过。在开发过程中,假设 "
Toboc
" 在第 4 行被错误地修改为 "Tobo
"。再次运行测试,它将失败。因此,数据校验和确保数据完整性。将 "Tobo
" 改回 "Toboc
",测试将再次通过。 - 预期架构:这检查架构,而不是像 "Data checksum" 那样检查数据。即使数据被修改但架构完好无损,此测试用例也将通过。
选择 "Press to configure" 选项,然后单击 "Select Connection",选择表。单击 "Retrieve",数据将显示。单击 "OK"。
为了演示这一点,再次将 "
Toboc
" 更新为 "Tobo
" 并运行所有用例。我们可以看到 "CompanyExpectedSchemaTest
" 通过了,而 "CompanyDataChecksumTest
" 失败了。在 company 表中添加新列,从而修改架构。
再次运行测试,它将失败。此选项确保架构得到维护。
- 行数:在属性中将 "
RowCount
" 设置为4
。运行测试。 - 执行时间:用于检查查询或存储过程的性能(稍后在文档中讨论)。选择 "Execution time" 作为选项,并保持 00:00:00.0010000。我们可以看到错误,因为执行时间超过了预期。
- 空结果集:用于验证是否从数据库接收到空结果集。如果返回的行数大于 0,则测试失败。
- 非空结果集:用于验证是否从数据库接收到空结果集。如果未返回任何行,则测试失败。例如,在存储过程或查询出错的情况下。
"Empty Result Set" 和 "Not Empty Result Set" 都在以不同的方式验证结果集。在这两种情况下,"
ResultSet
" 属性都是只读的,无法修改。 - 不确定:这是默认的测试条件,每当添加新测试用例时。包含此测试条件是为了表明尚未实现测试验证。在添加其他测试条件后,请从测试中删除此测试条件。
5. 理解代码后端的原理
现在,我们已经对 SQL 单元测试机制提供的选项数量有了很好的了解,我们将看看它在代码后端,即类中的工作原理。
SQL 设计器使我们能够轻松地编写和可视化测试用例,但在类中,它的行为与代码单元测试用例相同。参考 "CompanyRowCount
" SQL 单元测试用例。它有两个文件:resx 和 cs。Resx 文件包含查询详细信息,而 cs 类包含预期输出。
Resx 文件
Cs 文件
调试测试用例
将预期的总行数条件修改为 3。从测试资源管理器中选择一个测试用例。右键单击并选择 "Debug Selected Tests"。由于条件不匹配,将抛出带有详细信息的错误。此行为与代码单元测试中的 Assert 相同。
禁用测试用例
如果我们设置 Enabled: False,测试用例在执行期间将不会被测试,并显示为已通过。当测试用例已编写但相应功能的工作尚未完成时,可以使用此选项。稍后,随着项目的进展,我们可以启用并执行所有测试用例。
6. 验证存储过程、函数、视图和触发器
存储过程
在 SQL Server 中创建一个存储过程,该过程将向 department 表插入新行。
存储过程脚本
使用附加的 "Scripts" zip 文件夹中的 "StoredProcedure.txt"。
添加新测试用例 "DepartmentAdd
"。
- 选择 "预测试",计算 department 表中的记录数,并将预期值设置为
6
。 - 选择 "测试",并使用存储过程将数据插入
Department
表。 - 选择 "后测试"。再次验证计数,然后删除数据。通常,后测试用于清理用于测试的数据。如果我们不删除插入的行,下一次测试用例将失败,因为行数是
7
,而预测试说行数应该是6
。
因此,在测试存储过程等时,我们应该清理插入的临时数据并为下一次做好准备,这一点很重要。我们验证存储过程的目的已经达到。
函数:创建一个函数 "GetSearchTermCount
"。该函数根据搜索词返回记录数(执行 like 操作)。
函数脚本
使用附加的 "Scripts" zip 文件夹中的 "Function.txt"。
创建一个新测试用例并执行函数。由于 "Software Development" 和 "Management" 包含 "ent
",因此获取了 2 条记录。
视图
创建一个视图 "FetchDetails
"。
视图脚本
使用附加的 "Scripts" zip 文件夹中的 "View.txt"。
创建一个新测试用例并执行视图。将行数验证为 '9
'。
触发器
创建一个触发器,在更新 "SubDepartment
" 表时更新 "SubDepartmentLog
" 表。
触发器脚本
使用附加的 "Scripts" zip 文件夹中的 "Trigger.txt"。
创建一个新测试用例并更新 department
表。计算 "SubDepartmentLog
" 表中的行数。由于在更新 "SubDepartment
" 中的记录后触发器执行了一次,因此插入了一个新值。因此,返回的行数为 1
。