存储过程的功能测试 单元测试用例怎么写写

您的位置: >>
  作者水平有限,如有错误或纰漏,请指出,谢谢。
  背景介绍
  最近在团队在做release之前的regression,把各个feature分支merge回master之后发现DB的单元测试出现了20多个失败的test cases。之前没怎么做过DB的单元测试,正好借这个机会熟悉一下写DB单元测试的流程。
  这篇博文中首先介绍一下在我们的特定项目场景中是如何搭建DB 单元测试框架的,然后举一个简单的例子,从头到尾在visual studio中创建一个简单的单元测试工程。
  我们开发的产品使用的数据库为Sql Server,总共有400多张表,2000多个存储过程,每个存储过程都相当于应用代码中的一个功能函数。代码中的每个复杂的功能函数都可以通过写单元测试来在一定程度上保证代码质量,存储过程也如此。代码中的UT难点在于解耦,也就把相互牵连在一起的代码彼此分离开来,各个击破,例如A函数需要B函数提供的数据,测试A函数的时候我们只想测试A函数,不想调用B,这时候就需要我们自己提供B函数生成的数据。这叫做mock。
  在做DB单元测试的时候,存储过程所使用的数据比较特殊,都是持久化在数据库表中的,2000多个存储过程增删改查400多个表,我们需要把这些表的数据为每个存储过程做隔离,如果测试用例使用的数据相互之间关联,恐怕会天下大乱,因为在一般情况下,单元测试用例的运行顺序都是随机的,如果单元测试使用的数据有关联,很有可能两次运行结果也是随机的(但是有一种方法可以固定case执行顺序,我在最后的例子中进行说明),我们这次的20多个失败的cases就有这种原因导致的,两台机器上跑出的结果不一样,有的成功,有的失败。
  注:有关单元测试的定义,见另外一篇帖子,
  那么问题就来了,如何才能做数据的隔离呢?说一下我们的方案。
  准备数据
  我们创建了一个基准的数据库,做出一个备份,叫做base.bak,这个版本比较低,比如是2.8,这里面包含了一些测试的基本数据。然后我们创建了另外一个preparation的工程,用于把base.bak升级到当前release版本,例如,当前release的版本为2.18。这个工程同时也测试了升级的流程。升级成功之后,把这个数据库在本地做一个备份release_2_18.bak。好了,数据都准备好了。
  测试需要注意的要点
  四个函数
  对于微软的这个DB UT测试框架,有四个函数需要搞清楚,因为这可能影响你的测试结果:
[ClassInitialize]
public static void ClassInitialize(TestContext testContext)
[ClassCleanup]
public static void ClassCleanup()
[TestInitialize()]
public void TestInitialize()
[TestCleanup()]
public void TestCleanup()
顾名思义,ClassInitialize() 是在每个类初始化的时候被调用的
ClassCleanup() 是在类结束的时候,也就是一个类所有的case跑完的时候被调用的。
TestInitialize() 是在每个case跑之前被调用的。
TestCleanup() 是在每个case调用之后被调用的。
  对么?粗体的这句话不对,其余是对的。
  测试用例的运行是无序的,包含多个类的情况。
  看下面测试用例的之情情况你就明白了:
AssemblyInitialize
TestClass1: ClassInitialize
TestClass1: TestInitialize
TestClass1: MyTestCase1
TestClass1: TestCleanup
TestClass2: ClassInitialize
TestClass2: TestInitialize
TestClass2: MyTestCase2
TestClass2: TestCleanup
TestClass1: ClassCleanup
TestClass2: ClassCleanup
AssemblyCleanup
  ClassCleanup() 并不意味着TestClass1 的ClassCleanup 在这个类的最后一个case跑完之后被立即调用!事实上,它会等待所有case都被运行完之后,同TestClass2 的ClassCleanup 一块执行。
  具体原因看这个帖子,
  三个Action
  还是看下面的一个例子:
[TestMethod()]
public void Test_GetBasicRevenueByName()
SqlDatabaseTestActions testActions = this.SqlTest1D
// Execute the pre-test script
System.Diagnostics.Trace.WriteLineIf((testActions.PretestAction != null), "Executing pre-test script...");
SqlExecutionResult[] pretestResults = TestService.Execute(this.PrivilegedContext, this.PrivilegedContext, testActions.PretestAction);
// Execute the test script
System.Diagnostics.Trace.WriteLineIf((testActions.TestAction != null), "Executing test script...");
SqlExecutionResult[] testResults = TestService.Execute(this.ExecutionContext, this.PrivilegedContext, testActions.TestAction);
// Execute the post-test script
System.Diagnostics.Trace.WriteLineIf((testActions.PosttestAction != null), "Executing post-test script...");
SqlExecutionResult[] posttestResults = TestService.Execute(this.PrivilegedContext, this.PrivilegedContext, testActions.PosttestAction);
  每个测试用例中都会有三个action,这三个Action的用途如下:
PretestAction做的是测试前的准备工作,具体过程中可以为每个特定的case插入或更新测试需要的数据。
TestAction为调用存储过程进行测试,将实际结果和预期结果进行对比。
PosttestAction做的是测试完成后的清理工作,这里可以对PretestAction中的插入或者更新的数据进行回滚,恢复初始环境。
  最后的这个PosttestAction为我们的数据隔离提供了一种方法,所谓恢复初始环境的意思是执行一个case之前和之后数据库中的数据完全一样。
  这里有个问题,在PretestAction中进行数据插入还比较好恢复,如果是删除和更新呢?这就需要你记录下删除的和更新前的数据。太麻烦了。如果你的系统性能足够好,或者对运行UT的时间没有要求,可以用另外一种方法:restore DB。前面不是说过了么,我们在数据库升级之后做了一个备份,我们在这里使用它。在什么地方执行restoreDB?对,在TestCleanup() 中进行。
[TestInitialize()]
public void TestCleanup()
restoreDB();
  具体的流程就说完了,总结一下:
  准备数据库
  运行测试用例流程
  数据清理的两种方法
在PretestAction中添加数据恢复语句;
在TestCleanup()中restore DB。
  接下来我们从头到尾演示一下用VS2013 + SQL Server 2012是如何做数据库UT的。
  创建一个简单的数据库DBUTDemo
创建两张表。
create table EmployeeBasicInfo(
EmployeeNo int NOT NULL primary key,
Name nvarchar(50) NOT NULL,
TelephoneNum varchar(50) NOT NULL
create table EmployeeRevenue(
EmployeeNo int NOT NULL primary key,
BasicRevenue int NOT NULL,
MealSubsidy int NULL,
Bonus int NULL,
foreign key(EmployeeNo) references EmployeeBasicInfo(EmployeeNo)
创建一个存储过程
create procedure GetBasicRevenueByName(@name nvarchar(50))
select bi.Name,r.BasicRevenue from EmployeeRevenue r join EmployeeBasicInfo bi on r.EmployeeNo = bi.EmployeeNo where bi.Name = @name
  创建UT工程
点击File-&New-&Project...
选择Unit Test Project,输入工程名,选择创建路径,点击OK。
  添加一个类
右键DBUTDemo-&Add-&New Item...选择SQL Server Unit Test,输入名字,点击Add。
第一次添加数据库测试类需要配置数据库:点击New Connection。
  输入Server name,选择我们刚才创建的数据库DBUTDemo,点击Test Connection。如果成功会弹出对话框。连续两次点击OK。数据库配置就完成了。
  创建三个Actions
  点击Click here to create来创建TestAction,点击之后发现多了一个resx文件。
  输入下面的测试代码:
declare @return_value
nvarchar(50)EXEC
@return_value = [dbo].[GetBasicRevenueByName]
@name = N'three zhang'SELECT
'Return Value' = @return_value
  接下来创建另外两个Action:
  分别输入如下代码:
insert into EmployeeBasicInfo values(1,'three zhang',
'')insert into EmployeeBasicInfo values(2,'four li',
'')insert into EmployeeBasicInfo values(3,'simon', '')insert into EmployeeBasicInfo values(4,'jack',
'')insert into EmployeeRevenue values(1
,20000)insert into EmployeeRevenue values(2
,19000)insert into EmployeeRevenue values(3
,10000)insert into EmployeeRevenue values(4
delete from EmployeeRevenuedelete from EmployeeBasicInfo
  最后添加测试条件
  我们添加了两个测试条件,值可以在属性界面中修改:  第一个测试条件是在返回结果集1中,第一行第二列的期望值为30000,也就是three zhang的基本工资为30000。
  第二个测试条件测试结果集1非空。
  编译,运行
  编译成功后,打开Test Explorer,run我们刚才创建的case,测试通过。
  Ordered Test
  最后说下数据库测试用例如果需要固定的顺序该怎么办,微软提供了一种测试用例类型叫做Ordered Test:
  这种case是把几个case集合成为了一个,可以自己选择需要运行的普通的case,自己指定顺序。因为顺序固定了,这些cases中使用的数据就是可控的,因此在一个ordered case中的几个case可以共同使用某些数据,我们可以将数据隔离的单位由单个case变为几个case甚至一个类中的所有cases。
数据库热门文章
数据库最新文章本帖子已过去太久远了,不再提供回复功能。 上传我的文档
 下载
 收藏
该文档贡献者很忙,什么也没留下。
 下载此文档
正在努力加载中...
SQL存储过程测试
下载积分:500
内容提示:SQL存储过程测试
文档格式:PDF|
浏览次数:229|
上传日期: 12:50:32|
文档星级:
全文阅读已结束,如果下载本文需要使用
 500 积分
下载此文档
该用户还上传了这些文档
SQL存储过程测试
官方公共微信测试存储过程性能 - xuzhengzhu - 博客园
把握现在,领导未来
posts - 606, comments - 32, trackbacks - 18, articles - 0
create or replace view stats as
select 'STAT...' || a.name name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
select 'LATCH.' || name,
from v$latch
select 'STAT...Elapsed Time', hsecs from v$
创建查询统计信息视图
create global temporary table
RUN_STATS (
runid VARCHAR2(15),
VARCHAR2(80),
value INTEGER )
on commit delete
1)创建全局临时表
CREATE OR REPLACE PACKAGE runstats_pkg AS
PROCEDURE print_cputime(p_time_type VARCHAR2 DEFAULT 'start');
PROCEDURE rs_
PROCEDURE rs_
PROCEDURE rs_stop(p_difference_threshold IN NUMBER DEFAULT 0);
2)创建性能统计包
CREATE OR REPLACE PACKAGE BODY runstats_pkg AS
g_startt INTEGER;
--获取包处理过程中的cpu时间
PROCEDURE print_cputime(p_time_type VARCHAR2) IS
CASE lower(p_time_type)
WHEN 'start' THEN
dbms_output.put_line(g_start);
WHEN '1' THEN
dbms_output.put_line(g_run1);
WHEN '2' THEN
dbms_output.put_line(g_run2);
dbms_output.put_line('parameter is wrong');
--设置比较性能开始比较
PROCEDURE rs_start IS
DELETE FROM run_
INSERT INTO run_stats
SELECT 'before', stats.* FROM
:= dbms_utility.get_cpu_
g_startt := dbms_utility.get_
--设置比较性能第二个过程
PROCEDURE rs_middle IS
:= (dbms_utility.get_cpu_time - g_start);
g_runt1 := (dbms_utility.get_time - g_startt);
INSERT INTO run_stats
SELECT 'after 1', stats.* FROM
:= dbms_utility.get_cpu_
g_startt := dbms_utility.get_
--比较结束,并打印比较信息
PROCEDURE rs_stop(p_difference_threshold IN NUMBER DEFAULT 0) IS
v_rownum INTEGER := 0;
:= (dbms_utility.get_cpu_time - g_start);
g_runt2 := (dbms_utility.get_time - g_startt);
dbms_output.put_line('Run1 ran in ' || g_run1 || ' cpu hsecs' ||
' , elapsed time:' || g_runt1);
dbms_output.put_line('Run2 ran in ' || g_run2 || ' cpu hsecs' ||
' , elapsed time:' || g_runt2);
IF (g_run2 && 0) THEN
dbms_output.put_line('run 1 ran in ' ||
round(g_run1 / g_run2 * 100, 2) ||
'% of the time');
dbms_output.put_line(chr(9));
INSERT INTO run_stats
SELECT 'after 2', stats.* FROM
dbms_output.put_line(rpad('linenumber', 20) || rpad('Name', 50) ||
lpad('Run1', 12) || lpad('Run2', 12) ||
lpad('Diff', 12));
FOR x IN (SELECT rpad(a.name, 50) ||
to_char(b.value - a.value, '999,999,999') ||
to_char(c.value - b.value, '999,999,999') ||
to_char(((c.value - b.value) - (b.value - a.value)),
'999,999,999') data
FROM run_stats a, run_stats b, run_stats c
WHERE a.name = b.name
AND b.name = c.name
AND a.runid = 'before'
AND b.runid = 'after 1'
AND c.runid = 'after 2'
AND abs((c.value - b.value) - (b.value - a.value)) &
p_difference_threshold
ORDER BY abs((c.value - b.value) - (b.value - a.value))) LOOP
dbms_output.put_line(rpad(to_char(v_rownum), 20) || x.data);
v_rownum := v_rownum + 1;
v_rownum := 0;
dbms_output.put_line(chr(9));
dbms_output.put_line('Run1 latches total versus runs -- difference and pct');
dbms_output.put_line(rpad('linenumber', 20) || lpad('Run1', 12) ||
lpad('Run2', 12) || lpad('Diff', 12) ||
lpad('Pct', 10));
FOR x IN (SELECT to_char(run1, '999,999,999') ||
to_char(run2, '999,999,999') ||
to_char(diff, '999,999,999') ||
to_char(round(run1 /
decode(run2, 0, to_number(0), run2) * 100,
'99,999.99') || '%' data
FROM (SELECT SUM(b.value - a.value) run1,
SUM(c.value - b.value) run2,
SUM((c.value - b.value) - (b.value - a.value)) diff
FROM run_stats a, run_stats b, run_stats c
WHERE a.name = b.name
AND b.name = c.name
AND a.runid = 'before'
AND b.runid = 'after 1'
AND c.runid = 'after 2'
AND a.name LIKE 'LATCH%')) LOOP
dbms_output.put_line(rpad(to_char(v_rownum), 20) || x.data);
v_rownum := v_rownum + 1;
3)创建性能统计包体
CREATE OR REPLACE PROCEDURE proc_fortest1(i_x INTEGER DEFAULT 10,
i_y INTEGER DEFAULT 10,
i_z INTEGER DEFAULT 10) IS
PRAGMA AUTONOMOUS_TRANSACTION;
INTEGER := i_x;
INTEGER := i_y;
INTEGER := i_z;
VARCHAR2(1024) := 'insert into fort(i,j,k)values(:x,:y,:z)';
l_starttime DATE := SYSDATE;
FOR i IN 1 .. l_i LOOP
FOR j IN 1 .. l_j LOOP
FOR k IN 1 .. l_k LOOP
EXECUTE IMMEDIATE l_sql
USING i, j,
dbms_output.put_line('proc_fortest1 run times(s):' ||
(SYSDATE - l_starttime) * 24 * 60 * 60);
END proc_fortest1;
CREATE OR REPLACE PROCEDURE proc_fortest2(i_x INTEGER DEFAULT 10,
i_y INTEGER DEFAULT 10,
i_z INTEGER DEFAULT 10) IS
PRAGMA AUTONOMOUS_TRANSACTION;
INTEGER := i_x;
INTEGER := i_y;
INTEGER := i_z;
l_starttime DATE := SYSDATE;
VARCHAR2(1024) := 'INSERT INTO fort
SELECT i.rn, j.rn, k.rn
FROM (SELECT rownum rn FROM dual CONNECT BY rownum &= :l_i) i,
(SELECT rownum rn FROM dual CONNECT BY rownum &= :l_j) j,
(SELECT rownum rn FROM dual CONNECT BY rownum &= :l_k) k';
EXECUTE IMMEDIATE l_sql
USING l_i, l_j, l_k;
dbms_output.put_line('proc_fortest2 run times(s):' ||
(SYSDATE - l_starttime) * 24 * 60 * 60);
END proc_fortest2;
CREATE OR REPLACE PROCEDURE proc_fortest(i_limite_value INTEGER DEFAULT 0,
INTEGER DEFAULT 10,
INTEGER DEFAULT 10,
INTEGER DEFAULT 10) IS
runstats_pkg.rs_start();
proc_fortest1(i_x, i_y, i_z);
runstats_pkg.rs_middle();
proc_fortest2(i_x, i_y, i_z);
runstats_pkg.rs_stop(i_limite_value);
测试用例--存储过程
5、输出结果:
SQL&set serveroutput on SQL&set linesize 300 SQL&exec proc_fortest(0,10,20,30);
proc_fortest1 run times(s):0 proc_fortest2 run times(s):0
Run1 ran in 22 cpu hsecs , elapsed time:28
Run2 ran in 2 cpu hsecs , elapsed time:3 run 1 ran in 1100% of the time
linenumber
Run2 Diff 0
LATCH.library cache
pin allocation
STAT...change write time
STAT...physical read total multi block requests
STAT...messages sent
LATCH.OS process allocation
LATCH.SGA IO buffer pool latch
STAT...heap block compress
LATCH.flashback mapping
LATCH.KMG MMAN ready and startup request latch
LATCH.lgwr LWN SCN
LATCH.mostly latch-free SCN
LATCH.multiblock read objects
STAT...consistent gets - examination
STAT...cleanout - number of ktugct calls
STAT...active txn count during cleanout
STAT...deferred (CURRENT) block cleanout applicati
STAT...commit cleanouts successfully completed
STAT...commit cleanouts
LATCH.Consistent RBA
LATCH.undo global data
STAT...calls to kcmgcs
STAT...workarea executions - optimal
LATCH.library cache lock
STAT...consistent changes
STAT...user I/O wait time
STAT...physical reads cache prefetch
STAT...physical reads prefetch warmup
STAT...sorts (memory)
LATCH.redo writing
LATCH.redo allocation
LATCH.simulator lru latch
LATCH.messages
STAT...calls to get snapshot scn: kcmgss
STAT...consistent gets from cache
STAT...consistent gets
STAT...recursive cpu usage
STAT...CPU used by this session
LATCH.simulator hash latch
STAT...Elapsed Time
STAT...sorts (rows)
LATCH.cache buffers lru chain
STAT...free buffer requested
STAT...physical reads cache
LATCH.flashback allocation
STAT...physical reads
STAT...redo ordering marks
STAT...physical read IO requests
STAT...physical reads for flashback new
STAT...calls to kcmgas
STAT...physical read total IO requests
LATCH.object queue header operation
STAT...redo entries
STAT...recursive calls
STAT...execute count
STAT...session logical reads
STAT...db block gets
STAT...db block gets from cache
STAT...db block changes
-11,984 58
LATCH.shared pool
-11,997 59
LATCH.library cache pin
-24,005 60
LATCH.library cache
-24,009 61
LATCH.cache buffers chains
-30,410 62
STAT...physical read total bytes
-491,520 63
STAT...physical read bytes
-491,520 64
STAT...undo change vector size
-512,660 65
STAT...redo size
-1,495,196
Run1 latches total versus runs -- difference and pct linenumber
10,624.59%《软件测试自动化之道》读书笔记 之 SQL 存储过程测试
& & & & & & &
许多基于Windows的系统都使用了SQL Server作为后台组件。待测程序经常通过存储过程来访问数据库。对于这些应用场景,可以把SQL存储过程当成应用程序的辅助函数。有两种方法可以用来编写对SQL存储过程的自动化测试:
测试套件代码用T-SQL语言来编写,在类似于查询分析器(Query Analyzer)或者Management Studio这样的程序里执行
测试套件代码用.NET语言连编写
下面代码创建数据库&dbEmployees&;创建表&talEmployee&,插入相应数据;创建存储过程&usp_HireAfter&,创建登陆用户&employeesLogin&并赋予访问数据库和存储过程的权限:
makeDbEmployees.sql:
1 -- Database setup: makeDbEmployees.sql
3 use master
6 if exists (select * from sysdatabases where name='dbEmployees')
drop database dbEmployees
10 if exists (select * from syslogins where name = 'employeesLogin')
exec sp_droplogin 'employeesLogin'
14 create database dbEmployees
17 use dbEmployees
20 create table tblEmployees
empID char(3) primary key,
empLast varchar(35) not null,
empDOH datetime not null,
28 -- this is dev data, not test case data
29 insert into tblEmployees values('e11','Adams', '06/15/1998')
30 insert into tblEmployees values('e22','Baker', '06/15/2001')
33 exec sp_addlogin 'employeesLogin', 'September,2014'
35 exec sp_grantdbaccess 'employeesLogin'
38 create procedure usp_HiredAfter
@dt datetime
select * from tblEmployees where empDOH & @dt
44 grant execute on usp_HiredAfter to employeesLogin
47 -- 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
if exists (select * from sysdatabases where name='dbTestCasesAndResults')
drop database dbTestCasesAndResults
if exists (select * from syslogins where name = 'testLogin')
exec sp_droplogin 'testLogin'
create database dbTestCasesAndResults
use dbTestCasesAndResults
create table tblTestCases
caseID char(4) primary key,
input datetime not null,
expectedChecksum int not null
-- 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('0001','01/01/1998', 1042032)
insert into tblTestCases values('0002','01/01/1998', 9999999) -- deliberate error
insert into tblTestCases values('0003','01/01/2000', )
insert into tblTestCases values('0004','01/01/2006', 0)
create table tblResults
caseID char(4) not null,
result char(4) null,
whenRun datetime not null
exec sp_addlogin 'testLogin', 'September,2014'
exec sp_grantdbaccess 'testLogin'
grant select, insert, delete, update on tblTestCases to testLogin
grant select, insert, delete, update on tblResults to testLogin
-- end script
执行T-SQL脚本
运行T-SQL脚本,有好几种方法:
使用查询分析器;
使用批处理(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'的脚本:
1 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内容:
SQL_Latin1_General_CP1_CI_AS
expectedChecksum
上述内容中,
11.0是SQL Server2012的版本号;
3表示从第3行以后的内容是映射信息。
每个映射行有8列,前5列代表于输入数据有关的信息(本例中,指newData.dat),后3个列代表要导入的目标信息(本例中,指SQL表):
第一列其实是从第一列开始的一系列数字
第二列是要导入的数据的类型(当从文本文件导入时,不过什么值,都是SQLCHAR)
第三列是前缀长度(prefix length)
第四列是输入字段字符的最大长度
第五列是分隔符
第六列第七列分别指SQL表里相应列的顺序和名称
第八列指SQL排序规则(SQL_Latin1_General_CP1_CI_ASSQL默认排序规则)
Step3: 修改newData.dat,导入
修改后newData.dat内容:
2007-01-01 00:00:00.000
用BCP命令导入:
1 bcp.exe dbTestCasesAndResults.dbo.tblTestCases in newData.dat -f newData.fmt -S. -T
创建T-SQL&测试套件
使用SQL游标(cursor)遍历这个测试用例数据表。针对每个测试用例,调用待测存储过程并且取得它的返回值。把它的实际值于期望值进行比较,判定结果,保存测试结果。
SQL游标设计用来处理单个的数据行并不向其他SQL操作那个处理行集(rowset)。
首先声明一个指向保存测试数据的SQL表的游标
1 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语句于都数据库表的第一行:
1 open tCursor
2 fetch next
from tCursor
into @caseID, @input, @expectedChecksum
对第一行进行预读是为了对下面的循环进行控制,我们使用变量@@fetch_status来控制用于读取的这个循环,这个变量表示最近一次fetch操作的状态。如果操作成功,@@fetch_status值为0;若失败,@@fetch_status值为-1,-2。因此,可向下面这样遍历整个数据库表:
1 while @@fetch_status = 0
4 --运行测试用例
fetch next
from tCursor
into @caseID, @input, @expectedChecksum
在主循环内部,我们需要调用待测存储过程,并且把测试用力输入数据传给它。去会的值打印出来:
(注意:下面脚本调用存储过程&dbEmployees.dbo.usp_HiredAfter时,这里假设它只返回单个值)
1 exec @actual = dbEmployees.dbo.usp_HiredAfter @input
3 if(@actual=@expected)
set @resultLine=@caseID + ': Pass'
print @resultLine
set @resultLine=@caseID + ': Fail'
print @resultLine
使用完一个SQL游标之后,必须关闭这个游标并且调用deallocate命令把它作为一个资源释放:
1 close tCursor
2 deallocate tCursor
SQLspTest.sql测试脚本:
1 -- ===========================================================
2 -- TestAuto.sql
4 truncate table dbEmployees.dbo.tblEmployees
6 insert into dbEmployees.dbo.tblEmployees
values('e11','Adams', '06/15/1998')
8 insert into dbEmployees.dbo.tblEmployees
values('e22','Baker', '06/15/2001')
10 insert into dbEmployees.dbo.tblEmployees
values('e33','Young', '06/15/1998')
12 insert into dbEmployees.dbo.tblEmployees
values('e44','Zetta', '06/15/2001')
14 -- other data would be inserted too
17 declare tCursor cursor fast_forward
for select caseID, input, expectedChecksum
from dbTestCasesAndResults.dbo.tblTestCases
order by caseID
22 declare @caseID char(4), @input datetime, @expectedChecksum int
23 declare @whenRun datetime
24 declare @actualChecksum int
25 declare @resultLine varchar(50)
27 set @whenRun = getdate()
29 open tCursor
30 fetch next
from tCursor
into @caseID, @input, @expectedChecksum
34 while @@fetch_status = 0
exec @actualChecksum=dbEmployees.dbo.usp_HiredAfter @input
if (@actualChecksum = @expectedChecksum)
set @resultLine = @caseID + ' Pass'
print @resultLine
insert into dbTestCasesAndResults.dbo.tblResults values(@caseID, 'Pass', @whenRun)
set @resultLine = @caseID + ' Fail'
print @resultLine
insert into dbTestCasesAndResults.dbo.tblResults values(@caseID, 'Fail', @whenRun)
fetch next
from tCursor
into @caseID, @input, @expectedChecksum
58 close tCursor
59 deallocate tCursor
60 -- end script
当待测存储过程返回行集的时候,如何判断测试结果是否通过
待测存储过程脚本如下:
1 create procedure usp_HiredAfter
@dt datetime
select * from tblEmployees where empDOH & @dt
首先,应该创建一个临时表,用于保存存储过程返回的QL行集:
1 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
select @actualChecksum = checksum_agg(binary_checksum(*)) from #resultRowset
if (@actualChecksum = @expectedChecksum)
print 'Pass'
print 'Fail'
上面脚本中,内建的binary_checksum()函数返回表里的一行的校验和。checksum_agg()函数返回一组值的聚合校验和。这是待测存储过程返回行集的时候,判断测试是否通过的一种方法。
示例&SQLspTest.sql&脚本:
1 -- ===========================================================
2 -- Test automation harness: SQLspTest.sql
3 -- test dbEmployees..usp_HiredAfter
4 -- reads test case data and writes results
to dbTestCasesAndResults
7 set nocount on
9 if not exists
(select * from master.dbo.sysdatabases where name='dbTestCasesAndResults')
raiserror('Fatal error: dbTestCasesAndResults not found', 16, 1)
14 if exists (select * from sysobjects where name='tap_Reset')
drop procedure tap_Reset
18 create procedure tap_Reset
truncate table dbEmployees.dbo.tblEmployees
22 insert into dbEmployees.dbo.tblEmployees
values('e11','Adams', '06/15/1998')
24 insert into dbEmployees.dbo.tblEmployees
values('e22','Baker', '06/15/2001')
26 insert into dbEmployees.dbo.tblEmployees
values('e33','Young', '06/15/1998')
28 insert into dbEmployees.dbo.tblEmployees
values('e44','Zetta', '06/15/2001')
30 -- other data would be inserted too
33 -- prepare dbEmployees with rich data
34 exec tap_Reset
37 declare tCursor cursor fast_forward
for select caseID, input, expectedChecksum
from dbTestCasesAndResults.dbo.tblTestCases
order by caseID
42 declare @caseID char(4), @input datetime, @expectedChecksum int
43 declare @whenRun datetime
44 declare @resultMsg varchar(80)
45 declare @actualChecksum int
47 create table #resultRowset -- for checksum technique
empID char(3) primary key,
empLast varchar(35) not null,
empDOH datetime not null,
54 set @whenRun = getdate()
56 print 'Stored procedure under test = usp_HiredAfter'
57 print ' '
58 print 'CaseID
Expected Actual
59 print '==============================================='
61 open tCursor
62 fetch next
from tCursor
into @caseID, @input, @expectedChecksum
66 while @@fetch_status = 0
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
select @actualChecksum = checksum_agg(binary_checksum(*)) from #resultRowset
if (@actualChecksum = @expectedChecksum)
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',
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',
fetch next
from tCursor
into @caseID, @input, @expectedChecksum
106 close tCursor
107 deallocate tCursor
109 drop table #resultRowset
111 -- end script
当待测存储过程返回out参数时,如何判断测试结果是否通过
待测存储过程脚本如下:
1 create procedure usp_GetLast
@empID char(3)
@empLast varchar(35) out
select @empLast =empLast from tblEmployees where empID = @empID
return @@rowcount
测试这个存储过程的脚本如下:
1 declare @input char(3)
2 declare @empLat varchar(35)
3 declare @retval int
5 declare @expectedLast varchar(35)
6 declare @expectedRet int
8 set @input = 'e22'
9 set @expectedLast = 'Baker'
10 set @expectedRet=1
12 exec @retval =dbEmployees.dbo.usp_GetLast @input, @empLat out
13 if(@retval=@expectedRet and @empLat = @expectedLast)
print 'Pass'
print 'Fail'
SQL存储过程有一个常用的设计模式,即存储过程可以通过参数返回一个或多个值。当存储过程返回的值不是int类型时,这个模式是必须的,因为return关键字只接受int类型的变量。
当待测存储过程没有返回值时,如何判断测试结果是否通过
待测存储过程脚本如下:
1 create procedure usp_DeleteEmployee
@empID char(3)
delete from dbEmployees.dbo.tblEmployees where empID=@empID
测试这个存储过程的脚本如下:
1 declare @input char(3)
3 declare @expected int
4 declare @actual int
6 set @input = 'e22'
7 set @expected =
9 exec dbEmployees.dbo.usp_DeleteEmployee @input
10 select @actual=checksum_agg(checksum(*)) from dbEmployees.dbo.tblEmployees
11 if(@actual=@expected)
print 'Pass'
print 'Fail'
阅读(...) 评论()}

我要回帖

更多关于 自动化测试用例怎么写 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信