问题
如何测试没有返回值的存储过程。
设计
调用待测存储过程,然后计算该存储过程影响的对象的聚合校验和。把计算出来的校验和与期望的校验和进行比较。
方案
例如,设想待测存储过程要从tb1Employees表中删除特定的雇员记录:
create procedure usp_DeleteEmployee
@empID char(3)
as
delete from tb1Employees where empID = @empID
要测试这个存储过程,可以先调用它,计算tb1Employees表的校验和,然后把计算结果与期望值进行比较:
declare @input char(3)
declare @expected int
declare @actual int
set @input = 'e22'
set @expected = 15087775
exec dbEmployees.dbo.usp_DeleteEmployee 'e22'
select @actual = checksum_agg(checksum(*)) from dbEmployees.dbo.tb1.tb1Epmloyees
if(@actual = @expected)
print 'Pass'
else
print 'FAIL'
如果存储过程没有返回值,那它肯定要实施一些操作,比如从数据库里面删除一些数据,要测试这样的存储过程,需要比较存储过程所操作的对象的实际值和期望值。这种情况与测试返回SQL行集对象的存储过程非常类似。
注解
许多存储过程都会影响到底层的数据库表,最明显的例子包括那些使用INSERT、DELETE或UPDATA语句的存储过程。当测试这些存储过程的时候,在每次调用测试套件之前,必须首先把底层数据库表的状态设为某个已知状态。例如,假设要测试下面的存储过程:
usp_DeleteEmployee() defined as
create procedure usp_DeleteEmployee
@empID char(3)
as
delete from tb1Employees where empID = @empID
return @@rowcount
go
如果测试套件代码如下:
declare @input char(3)
declare @actualRows int
——主测试循环
——把测试用例数据读入@caseID,@input,@expectedRows
exec @actualRows = dbEmployees.dbo.usp_DeleteEmployee @input
——判断通过与否
——存储或显示测试用例结果
——主循环结束
那么在主循环中进行遍历的时候,每次所测试的数据库状态是不一样的,这会让判断期望值非常困难。因此需要在每次调用待测存储过程之前重设数据库状态:
declare @input char(3)
declare @actualRows int
——主测试循环
——把测试用例数据读入@caseID,@input,@expectedRows
truncate table dbEmployees.dbo.tb1Employees
insert into dbEmployees.dbo.tb1Employees values('001','Adams','10/10/2009')
insert into dbEmployees.dbo.tb1Employees values('002','Baker','10/10/2009')
——etc
exec @actualRows = dbEmployees.dbo.usp_DeleteEmployee @input
——判断通过与否
——存储或显示测试用例结果
——主循环结束
大多数情况下,重设数据库状态需要很多条语句,这会让测试套件脚本非常冗长。因此,好的做法是在测试套件里写一个辅助的存储过程用于处理每次调用待测存储过程之前重设数据库的状态
if exists(select * from sysobjects where name = 'tap_Reset')
drop procedure tap_Reset
go
create procedure tap_Reset
as
truncate table dbEmployees.dbo.tbqEmployees
insert into dbEmployees.dbo.tb1Employees values('001','Adams','10/10/2009')
insert into dbEmployees.dbo.tb1Employees values('002','Baker','10/10/2009')
——其他数据在此插入
go
这里,创建了一个用于自动化测试的小程序,这个程序先删除tb1Employees表里的所有数据,然后填充大量的测试平台数据,脚本可以在主测试循环里每次调用待测存储过程之前调用这个存储过程。如果不使用硬编码的INSERT语句来产生目标数据库表,还可以用BULK INSERT语句结合一个外部数据存储来做这件事情。
不要因为此处讨论就误以为应该总是把待测数据库重设为某个初始状态。还必须要处理某些测试场景需要针对不同的系统状态进行操作的情况。例如,某个测试场景可能先插入5个数据行,然后删除新加入的一行和原来数据中的一行,接下来再插入3个新行,然后再删除一行,这种情况下,应该检查数据库的每个状态是否正确,从而判断整个测试场景通过与否。