《软件测试自动化之道》读书笔记 之 SQL 存储过程测试

2014-09-28

待测程序
测试程序
  创建测试用例以及测试结果存储
  执行T-SQL脚本
  使用BCP工具导入测试用例数据
  创建T-SQL 测试套件
  当待测存储过程返回行集的时候,如何判断测试结果是否通过
  当待测存储过程返回out参数时,如何判断测试结果是否通过
  当待测存储过程没有返回值时,如何判断测试结果是否通过

许多基于Windows的系统都使用了SQL Server作为后台组件。待测程序经常通过存储过程来访问数据库。对于这些应用场景,可以把SQL存储过程当成应用程序的辅助函数。有两种方法可以用来编写对SQL存储过程的自动化测试:

  • 测试套件代码用T-SQL语言来编写,在类似于查询分析器(Query Analyzer)或者Management Studio这样的程序里执行
  • 测试套件代码用.NET语言连编写

待测程序


 返回

下面代码创建数据库‘dbEmployees’;创建表‘talEmployee’,插入相应数据;创建存储过程‘usp_HireAfter’,创建登陆用户‘employeesLogin’并赋予访问数据库和存储过程的权限:

makeDbEmployees.sql:

 -- Database setup: makeDbEmployees.sql 

 use master
go if exists (select * from sysdatabases where name='dbEmployees')
drop database dbEmployees
go if exists (select * from syslogins where name = 'employeesLogin')
exec sp_droplogin 'employeesLogin'
go create database dbEmployees
go use dbEmployees
go create table tblEmployees
(
empID char(3) primary key,
empLast varchar(35) not null,
empDOH datetime not null,
)
go -- this is dev data, not test case data
insert into tblEmployees values('e11','Adams', '06/15/1998')
insert into tblEmployees values('e22','Baker', '06/15/2001')
go exec sp_addlogin 'employeesLogin', 'September,2014'
go
exec sp_grantdbaccess 'employeesLogin'
go create procedure usp_HiredAfter
@dt datetime
as
select * from tblEmployees where empDOH > @dt
go grant execute on usp_HiredAfter to employeesLogin
go -- end script

注意:当测试SQL存储过程时,有两个理由使你最好不要使用用于开发的数据库进行测试:

  • 测试存储过程有时会修改包含这个存储过程的数据库
  • 开发的那个数据库通常没有足够多的数据或者这些数据不是为了测试的目的而设计的

SQL数据库支持两种不同的安全模式:使用Windows认证可通过Windows账号ID和密码连接数据库,使用混合模式认证可以通过SQL登陆ID和SQL密码来连接数据库。若想通过SQL认证来连接测数据库,应该使用系统存储过程sp_addlogin()创建SQL登陆账号和密码。

SQL登陆账号和SQL用户区别:

  • SQL登陆账号是服务器范围的对象,它用来控制针对装有SQL Server的机器的连接许可;
  • SQL用户是数据库方位的对象,他用来控制数据库以及他所包含的表、存储过程和其他一些对象的权限许可。

当为一个SQL登陆账号分配权限的时候,会自动创建一个名为SQL用户。所以最终会有一个SQL登陆账号和一个SQL用户,两个名字相同且相互关联。当然也可以让不同名字的登陆账号和用户相互关联。

测试程序


返回

创建测试用例以及测试结果存储

以下T-SQL脚本,创建一个数据库然后创建一些表用来保存测试用例的输入数据和测试结果;创建一个专用SQL登陆账号,赋予一定权限:

makeDbTestCasesAndResults.sql:

  -- Test case data and results setup: makeDbTestCasesAndResults.sql

  use master
go if exists (select * from sysdatabases where name='dbTestCasesAndResults')
drop database dbTestCasesAndResults
go if exists (select * from syslogins where name = 'testLogin')
exec sp_droplogin 'testLogin'
go create database dbTestCasesAndResults
go use dbTestCasesAndResults
go create table tblTestCases
(
caseID char(4) primary key,
input datetime not null,
expectedChecksum int not null
)
go -- this is the test case data for usp_HiredAfter using a checksum expected
-- value approach
-- can also read from a text file using BCP, DTS, or a C# program
insert into tblTestCases values('','01/01/1998', 1042032)
insert into tblTestCases values('','01/01/1998', 9999999) -- deliberate error
insert into tblTestCases values('','01/01/2000', 25527856)
insert into tblTestCases values('','01/01/2006', 0)
go create table tblResults
(
caseID char(4) not null,
result char(4) null,
whenRun datetime not null
)
go exec sp_addlogin 'testLogin', 'September,2014'
go
exec sp_grantdbaccess 'testLogin'
go grant select, insert, delete, update on tblTestCases to testLogin
go grant select, insert, delete, update on tblResults to testLogin
go -- end script

执行T-SQL脚本

运行T-SQL脚本,有好几种方法:

  • 使用查询分析器;
  • 使用osql.exe;
  • 使用批处理(BAT)

下面使用osql.exe程序使用以下命令执行这个脚本:

osql.exe -S(local) -U loginID -P loginPassword -i makeDbTestCasesAndResults.sql -n > RESULT.txt

osql.exe -S(local) -E -i makeDbTestCasesAndResults.sql -n > RESULT.txt

-E表示使用Windows认证模式。

注意:osql.exe的参数是大小写敏感的。

使用BCP工具导入测试用例数据

创建一个BCP格式的文件,用于把你想要导入的文本文件信息映射到目标SQL表中。然后把上述格式的文件作为参数传给命令行工具bcp.exe

Step 1: 先看一下如何从表‘tblTestCases’中导出数据

create table 'tblTestCases'的脚本:

 create table tblTestCases
(
caseID char(4) primary key,
input datetime not null,
expectedChecksum int not null
)

用BCP导出表‘tblTestCases’到dat文件中:

bcp dbTestCasesAndResults.dbo.tblTestCases out C:\Code\AutomationTest\newData.dat -c -T

图1导出的数据

Step 2: 用BCP导出格式文件

EXEC master..xp_cmdshell 'bcp dbTestCasesAndResults.dbo.tblTestCases format nul -f C:\Code\AutomationTest\newData.fmt -c -T'

newData.fmt内容:

 11.0
3
1 SQLCHAR 0 4 "\t" 1 caseID SQL_Latin1_General_CP1_CI_AS
2 SQLCHAR 0 24 "\t" 2 input ""
3 SQLCHAR 0 12 "\r\n" 3 expectedChecksum ""

上述内容中,

  1. 11.0是SQL Server2012的版本号;
  2. 3表示从第3行以后的内容是映射信息。
  3. 每个映射行有8列,前5列代表于输入数据有关的信息(本例中,指newData.dat),后3个列代表要导入的目标信息(本例中,指SQL表):
  • 第一列其实是从第一列开始的一系列数字
  • 第二列是要导入的数据的类型(当从文本文件导入时,不过什么值,都是SQLCHAR)
  • 第三列是前缀长度(prefix length)
  • 第四列是输入字段字符的最大长度
  • 第五列是分隔符
  • 第六列第七列分别指SQL表里相应列的顺序和名称
  • 第八列指SQL排序规则(SQL_Latin1_General_CP1_CI_ASSQL默认排序规则)

Step3: 修改newData.dat,导入

修改后newData.dat内容:

000    2007-01-01 00:00:00.000    7

用BCP命令导入:

 bcp.exe dbTestCasesAndResults.dbo.tblTestCases in newData.dat -f newData.fmt -S. -T

创建T-SQL 测试套件

使用SQL游标(cursor)遍历这个测试用例数据表。针对每个测试用例,调用待测存储过程并且取得它的返回值。把它的实际值于期望值进行比较,判定结果,保存测试结果。

SQL游标设计用来处理单个的数据行并不向其他SQL操作那个处理行集(rowset)。

首先声明一个指向保存测试数据的SQL表的游标

 declare tCursor cursor fast_forward
for select caseID, input, expectedChecksum
from dbTestCasesAndResults.dbo.tblTestCases
order by caseID

注意:游标于其它SQL变量不同,游标变量的名字前面并没有@字符。可供声明游标的有好几种。FAST_FORWARD最适合用来读取测试用例数据,它实际上就是FORWAR_ONLY和READ_ONLY的别名。
在使用游标前,必须先打开游标。然后,如果想要遍历整个数据库表,则必须通过fetch next语句于都数据库表的第一行:

 open tCursor
fetch next
from tCursor
into @caseID, @input, @expectedChecksum

对第一行进行预读是为了对下面的循环进行控制,我们使用变量@@fetch_status来控制用于读取的这个循环,这个变量表示最近一次fetch操作的状态。如果操作成功,@@fetch_status值为0;若失败,@@fetch_status值为-1,-2。因此,可向下面这样遍历整个数据库表:

 while @@fetch_status = 0
begin --运行测试用例 fetch next
from tCursor
into @caseID, @input, @expectedChecksum
end

在主循环内部,我们需要调用待测存储过程,并且把测试用力输入数据传给它。去会的值打印出来:

(注意:下面脚本调用存储过程‘dbEmployees.dbo.usp_HiredAfter时,这里假设它只返回单个值)

 exec @actual = dbEmployees.dbo.usp_HiredAfter @input

 if(@actual=@expected)
begin
set @resultLine=@caseID + ': Pass'
print @resultLine
end
else
begin
set @resultLine=@caseID + ': Fail'
print @resultLine
end

使用完一个SQL游标之后,必须关闭这个游标并且调用deallocate命令把它作为一个资源释放:

 close tCursor
deallocate tCursor

SQLspTest.sql测试脚本:

 -- ===========================================================
-- TestAuto.sql truncate table dbEmployees.dbo.tblEmployees insert into dbEmployees.dbo.tblEmployees
values('e11','Adams', '06/15/1998')
insert into dbEmployees.dbo.tblEmployees
values('e22','Baker', '06/15/2001')
insert into dbEmployees.dbo.tblEmployees
values('e33','Young', '06/15/1998')
insert into dbEmployees.dbo.tblEmployees
values('e44','Zetta', '06/15/2001')
-- other data would be inserted too declare tCursor cursor fast_forward
for select caseID, input, expectedChecksum
from dbTestCasesAndResults.dbo.tblTestCases
order by caseID declare @caseID char(4), @input datetime, @expectedChecksum int
declare @whenRun datetime
declare @actualChecksum int
declare @resultLine varchar(50) set @whenRun = getdate() open tCursor
fetch next
from tCursor
into @caseID, @input, @expectedChecksum while @@fetch_status = 0
begin exec @actualChecksum=dbEmployees.dbo.usp_HiredAfter @input if (@actualChecksum = @expectedChecksum)
begin
set @resultLine = @caseID + ' Pass'
print @resultLine
insert into dbTestCasesAndResults.dbo.tblResults values(@caseID, 'Pass', @whenRun)
end
else
begin
set @resultLine = @caseID + ' Fail'
print @resultLine
insert into dbTestCasesAndResults.dbo.tblResults values(@caseID, 'Fail', @whenRun)
end fetch next
from tCursor
into @caseID, @input, @expectedChecksum end close tCursor
deallocate tCursor
-- end script

当待测存储过程返回行集的时候,如何判断测试结果是否通过

待测存储过程脚本如下:

 create procedure usp_HiredAfter
@dt datetime
as
select * from tblEmployees where empDOH > @dt

首先,应该创建一个临时表,用于保存存储过程返回的QL行集:

 create table #resultRowset
(
empID char(3) primary key,
empLast varchar(35) not null,
empDOH datetime not null,
)

然后,我们可以调用待测存储过程并把返回的行集存入临时表:

  insert #resultRowset (empID, empLast, empDOH) -- call sp under test
exec dbEmployees.dbo.usp_HiredAfter @input

接下来,我们计算临时表的聚合校验,并把实际值和期望值进行比较:

 if (@@rowcount = 0)
set @actualChecksum =0
else
select @actualChecksum = checksum_agg(binary_checksum(*)) from #resultRowset
if (@actualChecksum = @expectedChecksum)
print 'Pass'
else
print 'Fail'

上面脚本中,内建的binary_checksum()函数返回表里的一行的校验和。checksum_agg()函数返回一组值的聚合校验和。这是待测存储过程返回行集的时候,判断测试是否通过的一种方法。

示例‘SQLspTest.sql’脚本:

 -- ===========================================================
-- Test automation harness: SQLspTest.sql
-- test dbEmployees..usp_HiredAfter
-- reads test case data and writes results
-- to dbTestCasesAndResults set nocount on if not exists
(select * from master.dbo.sysdatabases where name='dbTestCasesAndResults')
raiserror('Fatal error: dbTestCasesAndResults not found', 16, 1)
go if exists (select * from sysobjects where name='tap_Reset')
drop procedure tap_Reset
go create procedure tap_Reset
as
truncate table dbEmployees.dbo.tblEmployees insert into dbEmployees.dbo.tblEmployees
values('e11','Adams', '06/15/1998')
insert into dbEmployees.dbo.tblEmployees
values('e22','Baker', '06/15/2001')
insert into dbEmployees.dbo.tblEmployees
values('e33','Young', '06/15/1998')
insert into dbEmployees.dbo.tblEmployees
values('e44','Zetta', '06/15/2001')
-- other data would be inserted too
go -- prepare dbEmployees with rich data
exec tap_Reset
go declare tCursor cursor fast_forward
for select caseID, input, expectedChecksum
from dbTestCasesAndResults.dbo.tblTestCases
order by caseID declare @caseID char(4), @input datetime, @expectedChecksum int
declare @whenRun datetime
declare @resultMsg varchar(80)
declare @actualChecksum int create table #resultRowset -- for checksum technique
(
empID char(3) primary key,
empLast varchar(35) not null,
empDOH datetime not null,
) set @whenRun = getdate() print 'Stored procedure under test = usp_HiredAfter'
print ' '
print 'CaseID Input Expected Actual Result'
print '===============================================' open tCursor
fetch next
from tCursor
into @caseID, @input, @expectedChecksum while @@fetch_status = 0
begin exec tap_Reset -- reset test bed data truncate table #resultRowset -- empty out the result rowset insert #resultRowset (empID, empLast, empDOH) -- call sp under test
exec dbEmployees.dbo.usp_HiredAfter @input if (@@rowcount = 0)
set @actualChecksum = 0
else
select @actualChecksum = checksum_agg(binary_checksum(*)) from #resultRowset if (@actualChecksum = @expectedChecksum)
begin
set @resultMsg = @caseID + ' ' + cast(@input as varchar(11)) +
' ' + cast(@expectedChecksum as varchar(20)) + ' ' +
cast(@actualChecksum as varchar(20)) + ' Pass'
print @resultMsg
insert into dbTestCasesAndResults.dbo.tblResults values(@caseID, 'Pass',
@whenRun)
end
else
begin
set @resultMsg = @caseID + ' ' + cast(@input as varchar(11)) +
' ' + cast(@expectedChecksum as varchar(20)) + ' ' +
cast(@actualChecksum as varchar(20)) + ' FAIL'
print @resultMsg
insert into dbTestCasesAndResults.dbo.tblResults values(@caseID, 'FAIL',
@whenRun)
end fetch next
from tCursor
into @caseID, @input, @expectedChecksum end close tCursor
deallocate tCursor drop table #resultRowset -- end script

当待测存储过程返回out参数时,如何判断测试结果是否通过

待测存储过程脚本如下:

 create procedure usp_GetLast
@empID char(3)
@empLast varchar(35) out
as
select @empLast =empLast from tblEmployees where empID = @empID
return @@rowcount

测试这个存储过程的脚本如下:

 declare @input char(3)
declare @empLat varchar(35)
declare @retval int declare @expectedLast varchar(35)
declare @expectedRet int set @input = 'e22'
set @expectedLast = 'Baker'
set @expectedRet=1 exec @retval =dbEmployees.dbo.usp_GetLast @input, @empLat out
if(@retval=@expectedRet and @empLat = @expectedLast)
print 'Pass'
else
print 'Fail'

注解:

SQL存储过程有一个常用的设计模式,即存储过程可以通过参数返回一个或多个值。当存储过程返回的值不是int类型时,这个模式是必须的,因为return关键字只接受int类型的变量。

当待测存储过程没有返回值时,如何判断测试结果是否通过

待测存储过程脚本如下:

 create procedure usp_DeleteEmployee
@empID char(3)
as
delete from dbEmployees.dbo.tblEmployees where empID=@empID

测试这个存储过程的脚本如下:

 declare @input char(3)

 declare @expected int
declare @actual int set @input = 'e22'
set @expected = 150847775 exec dbEmployees.dbo.usp_DeleteEmployee @input
select @actual=checksum_agg(checksum(*)) from dbEmployees.dbo.tblEmployees
if(@actual=@expected)
print 'Pass'
else
print 'Fail'

参考

[1] bcp命令详解

《软件测试自动化之道》读书笔记 之 SQL 存储过程测试的更多相关文章

  1. 《软件测试自动化之道》读书笔记 之 基于反射的UI测试

    <软件测试自动化之道>读书笔记 之 基于反射的UI测试 2014-09-24 测试自动化程序的任务待测程序测试程序  启动待测程序  设置窗体的属性  获取窗体的属性  设置控件的属性  ...

  2. 《软件测试自动化之道》读书笔记 之 基于Windows的UI测试

    <软件测试自动化之道>读书笔记 之 基于Windows的UI测试 2014-09-25 测试自动化程序的任务待测程序测试程序  启动待测程序  获得待测程序主窗体的句柄  获得有名字控件的 ...

  3. 《软件测试自动化之道》读书笔记 之 底层的Web UI 测试

    <软件测试自动化之道>读书笔记 之 底层的Web UI 测试 2014-09-28 测试自动化程序的任务待测程序测试程序  启动IE并连接到这个实例  如何判断待测web程序完全加载到浏览 ...

  4. 《软件测试自动化之道》读书笔记 之 XML测试

    <软件测试自动化之道>读书笔记 之 XML测试 2014-10-07 待测程序测试程序  通过XmlTextReader解析XML  通过XmlDocument解析XML  通过XmlPa ...

  5. 读书笔记之SQL注入漏洞和SQL调优

    原文:读书笔记之SQL注入漏洞和SQL调优 最近读了程序员的SQL金典这本书,觉得里面的SQL注入漏洞和SQL调优总结得不错,下面简单讨论下SQL注入漏洞和SQL调优. 1. SQL注入漏洞 由于“' ...

  6. 读书笔记汇总 - SQL必知必会(第4版)

    本系列记录并分享学习SQL的过程,主要内容为SQL的基础概念及练习过程. 书目信息 中文名:<SQL必知必会(第4版)> 英文名:<Sams Teach Yourself SQL i ...

  7. 【MySQL 读书笔记】SQL 刷脏页可能造成数据库抖动

    开始今天读书笔记之前我觉得需要回顾一下当我们在更新一条数据的时候做了什么. 因为 WAL 技术的存在,所以当我们执行一条更新语句的时候是先写日志,后写磁盘的.当我们在内存中写入了 redolog 之后 ...

  8. 【读书笔记】--SQL基础概念复习

    主键:每个表,只能有一个主键,主键不能为NULL,且必须是唯一的.主键最好是由单个列组成,但这不是必须的,主键也可以是由多个列组成,如果表的两个列组合在一起能唯一标识一个行,而单个列不能,则可以将这两 ...

  9. <<google软件测试之道>>读书笔记

    以前一直从开发的角度来看待测试,看完这本书以后感觉错了,难怪之前公司的测试一直搭建不起来 1.开发人员,开发测试人员,测试人员 * 开发人员负责开发 * 开发测试人员近距离接触代码,负责编写测试用例, ...

随机推荐

  1. C# 判断输入的字符串是否只包含数字和英文字母

    /// <summary> /// 判断输入的字符串是否只包含数字和英文字母 /// </summary> /// <param name="input&quo ...

  2. 硬件篇之MMU

    <背景> MMU即内存管理单元(Memory Manage Unit),是一个与软件密切相关的硬件部件,也是理解linux等操作系统内核机制的最大障碍之一.可以说,不懂MMU使很多人一直停 ...

  3. SQL SERVER字符串中的空格去除

    1.LTRIM 删除起始空格后返回字符表达式. 语法 LTRIM   (   character_expression   ) 参数 character_expression 是字符或二进制数据表达式 ...

  4. 8.9 正睿暑期集训营 Day6 C 风花雪月(DP)

    题目链接 完整比赛在这儿. 杜老师tql . 求期望要抽卡的次数,也就是求期望经历了多少不满足状态.而每个不满足的状态对答案的贡献为\(1\),所以可以直接算概率.即\(Ans=\sum_{不满足状态 ...

  5. Syntax error , insert “EnumBody” to complete EnumDeclaration

    当@Test写在方法前面的时候因为没有导入junit的jar包,如果已经导入架包依然是同样的错误,那就是方法没写对,或者还没有写方法

  6. ${pageContext.request.contextPath}无法解析

    摘要 突然出现无法解析${pageContext.request.contextPath}的问题,在点击<a href="${pageContext.request.contextPa ...

  7. sqlite 一条记录判断一个字段是否like另一个字段

    sql: select * from test where col2 like '%'||col1||'%';

  8. iphone程序适配ipad可以用下面的宏进行尺寸改写

    //判断是否为iPad #define ISIPAD [[[UIDevice currentDevice].model substringToIndex:4] isEqualToString:@&qu ...

  9. C#编程(七十一)---------- 自定义特性

    自定义特性 在说自定义之前,有必要先介绍一些基本的概念. 元数据:就是C#中封装的一些类,无法修改,类成员的特性被称为元数据中的注释 1.什么是特性? (1)属性和特性的区别 属性:属性是面向对象思想 ...

  10. 移动端web禁止长按选择文字以及弹出菜单

    /*如果是禁用长按选择文字功能,用css*/ * { -webkit-touch-callout:none; -webkit-user-select:none; -khtml-user-select: ...