《軟件測試自動化之道》讀書筆記 之 SQL 存儲過程測試


《軟件測試自動化之道》讀書筆記 之 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:

 1 -- Database setup: makeDbEmployees.sql 
 2 
 3 use master
 4 go
 5 
 6 if exists (select * from sysdatabases where name='dbEmployees')
 7  drop database dbEmployees
 8 go
 9 
10 if exists (select * from syslogins where name = 'employeesLogin')
11  exec sp_droplogin 'employeesLogin'
12 go
13 
14 create database dbEmployees
15 go
16 
17 use dbEmployees
18 go
19 
20 create table tblEmployees
21 (
22  empID char(3) primary key,
23  empLast varchar(35) not null,
24  empDOH datetime not null,
25 )
26 go
27 
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')
31 go
32 
33 exec sp_addlogin 'employeesLogin', 'September,2014'
34 go
35 exec sp_grantdbaccess 'employeesLogin'
36 go
37 
38 create procedure usp_HiredAfter
39  @dt datetime
40 as
41  select * from tblEmployees where empDOH > @dt
42 go
43 
44 grant execute on usp_HiredAfter to employeesLogin
45 go
46 
47 -- end script
View Code

注意:當測試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:

 1  -- Test case data and results setup: makeDbTestCasesAndResults.sql
 2  
 3  use master
 4  go
 5  
 6  if exists (select * from sysdatabases where name='dbTestCasesAndResults')
 7   drop database dbTestCasesAndResults
 8  go
 9  
10  if exists (select * from syslogins where name = 'testLogin')
11   exec sp_droplogin 'testLogin'
12  go
13  
14  create database dbTestCasesAndResults
15  go
16  
17  use dbTestCasesAndResults
18  go
19  
20  create table tblTestCases
21  (
22   caseID char(4) primary key,
23   input datetime not null,
24   expectedChecksum int not null
25  )
26  go
27  
28  -- this is the test case data for usp_HiredAfter using a checksum expected
29  -- value approach
30  -- can also read from a text file using BCP, DTS, or a C# program
31  insert into tblTestCases values('0001','01/01/1998', 1042032)
32  insert into tblTestCases values('0002','01/01/1998', 9999999) -- deliberate error
33  insert into tblTestCases values('0003','01/01/2000', 25527856)
34  insert into tblTestCases values('0004','01/01/2006', 0)
35  go
36  
37  create table tblResults
38  (
39   caseID char(4) not null,
40   result char(4) null,
41   whenRun datetime not null
42  )
43  go
44  
45  exec sp_addlogin 'testLogin', 'September,2014'
46  go
47  exec sp_grantdbaccess 'testLogin'
48  go
49  
50  grant select, insert, delete, update on tblTestCases to testLogin
51  go
52  
53  grant select, insert, delete, update on tblResults to testLogin
54  go
55  
56  -- end script
View Code

 

執行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'的腳本:

1 create table tblTestCases
2 (
3  caseID char(4) primary key,  
4  input datetime not null,
5  expectedChecksum int not null
6 )
View Code

用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內容:

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

上述內容中,

  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命令導入:

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

創建T-SQL 測試套件

使用SQL游標(cursor)遍歷這個測試用例數據表。針對每個測試用例,調用待測存儲過程並且取得它的返回值。把它的實際值於期望值進行比較,判定結果,保存測試結果。

SQL游標設計用來處理單個的數據行並不向其他SQL操作那個處理行集(rowset)。

首先聲明一個指向保存測試數據的SQL表的游標

1 declare tCursor cursor fast_forward
2  for select caseID, input, expectedChecksum
3  from dbTestCasesAndResults.dbo.tblTestCases
4  order by caseID
View Code

注意:游標於其它SQL變量不同,游標變量的名字前面並沒有@字符。可供聲明游標的有好幾種。FAST_FORWARD最適合用來讀取測試用例數據,它實際上就是FORWAR_ONLY和READ_ONLY的別名。
在使用游標前,必須先打開游標。然后,如果想要遍歷整個數據庫表,則必須通過fetch next語句於都數據庫表的第一行:

1 open tCursor
2 fetch next 
3  from tCursor
4  into @caseID, @input, @expectedChecksum
View Code

對第一行進行預讀是為了對下面的循環進行控制,我們使用變量@@fetch_status來控制用於讀取的這個循環,這個變量表示最近一次fetch操作的狀態。如果操作成功,@@fetch_status值為0;若失敗,@@fetch_status值為-1,-2。因此,可向下面這樣遍歷整個數據庫表:

1 while @@fetch_status = 0
2 begin
3 
4 --運行測試用例
5 
6  fetch next
7   from tCursor
8   into @caseID, @input, @expectedChecksum
9 end
View Code

在主循環內部,我們需要調用待測存儲過程,並且把測試用力輸入數據傳給它。去會的值打印出來:

(注意:下面腳本調用存儲過程‘dbEmployees.dbo.usp_HiredAfter時,這里假設它只返回單個值)

 1 exec @actual = dbEmployees.dbo.usp_HiredAfter @input
 2 
 3 if(@actual=@expected)
 4   begin
 5     set @resultLine=@caseID + ': Pass'
 6     print @resultLine
 7   end
 8 else
 9   begin
10     set @resultLine=@caseID + ': Fail'
11     print @resultLine
12   end
View Code

使用完一個SQL游標之后,必須關閉這個游標並且調用deallocate命令把它作為一個資源釋放:

1 close tCursor
2 deallocate tCursor
View Code

SQLspTest.sql測試腳本:

 1 -- ===========================================================
 2 -- TestAuto.sql
 3 
 4 truncate table dbEmployees.dbo.tblEmployees
 5 
 6 insert into dbEmployees.dbo.tblEmployees
 7  values('e11','Adams', '06/15/1998')
 8 insert into dbEmployees.dbo.tblEmployees
 9  values('e22','Baker', '06/15/2001')
10 insert into dbEmployees.dbo.tblEmployees
11  values('e33','Young', '06/15/1998')
12 insert into dbEmployees.dbo.tblEmployees
13  values('e44','Zetta', '06/15/2001')
14 -- other data would be inserted too
15 
16 
17 declare tCursor cursor fast_forward
18  for select caseID, input, expectedChecksum
19  from dbTestCasesAndResults.dbo.tblTestCases
20  order by caseID
21 
22 declare @caseID char(4), @input datetime, @expectedChecksum int
23 declare @whenRun datetime
24 declare @actualChecksum int
25 declare @resultLine varchar(50)
26 
27 set @whenRun = getdate()
28 
29 open tCursor
30 fetch next 
31  from tCursor
32  into @caseID, @input, @expectedChecksum
33 
34 while @@fetch_status = 0
35 begin
36 
37  exec @actualChecksum=dbEmployees.dbo.usp_HiredAfter @input
38   
39  if (@actualChecksum = @expectedChecksum)
40   begin
41    set @resultLine = @caseID + ' Pass'
42    print @resultLine
43    insert into dbTestCasesAndResults.dbo.tblResults values(@caseID, 'Pass', @whenRun)
44   end
45  else
46   begin
47    set @resultLine = @caseID + ' Fail'
48    print @resultLine
49    insert into dbTestCasesAndResults.dbo.tblResults values(@caseID, 'Fail', @whenRun)
50   end
51 
52  fetch next
53   from tCursor
54   into @caseID, @input, @expectedChecksum
55  
56 end
57 
58 close tCursor
59 deallocate tCursor
60 -- end script
View Code

 

當待測存儲過程返回行集的時候,如何判斷測試結果是否通過

待測存儲過程腳本如下:

1 create procedure usp_HiredAfter
2  @dt datetime
3 as
4  select * from tblEmployees where empDOH > @dt
View Code

首先,應該創建一個臨時表,用於保存存儲過程返回的QL行集:

1 create table #resultRowset 
2 (
3  empID char(3) primary key,
4  empLast varchar(35) not null,
5  empDOH datetime not null,
6 )
View Code

然后,我們可以調用待測存儲過程並把返回的行集存入臨時表:

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

接下來,我們計算臨時表的聚合校驗,並把實際值和期望值進行比較:

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

上面腳本中,內建的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
  5 --  to dbTestCasesAndResults
  6 
  7 set nocount on
  8 
  9 if not exists
 10  (select * from master.dbo.sysdatabases where name='dbTestCasesAndResults')
 11  raiserror('Fatal error: dbTestCasesAndResults not found', 16, 1)
 12 go
 13 
 14 if exists (select * from sysobjects where name='tap_Reset')
 15  drop procedure tap_Reset
 16 go
 17 
 18 create procedure tap_Reset
 19 as
 20  truncate table dbEmployees.dbo.tblEmployees
 21 
 22 insert into dbEmployees.dbo.tblEmployees
 23  values('e11','Adams', '06/15/1998')
 24 insert into dbEmployees.dbo.tblEmployees
 25  values('e22','Baker', '06/15/2001')
 26 insert into dbEmployees.dbo.tblEmployees
 27  values('e33','Young', '06/15/1998')
 28 insert into dbEmployees.dbo.tblEmployees
 29  values('e44','Zetta', '06/15/2001')
 30 -- other data would be inserted too
 31 go
 32 
 33 -- prepare dbEmployees with rich data
 34 exec tap_Reset
 35 go
 36 
 37 declare tCursor cursor fast_forward
 38  for select caseID, input, expectedChecksum
 39  from dbTestCasesAndResults.dbo.tblTestCases
 40  order by caseID
 41 
 42 declare @caseID char(4), @input datetime, @expectedChecksum int
 43 declare @whenRun datetime
 44 declare @resultMsg varchar(80)
 45 declare @actualChecksum int
 46 
 47 create table #resultRowset -- for checksum technique
 48 (
 49  empID char(3) primary key,
 50  empLast varchar(35) not null,
 51  empDOH datetime not null,
 52 )
 53 
 54 set @whenRun = getdate()
 55 
 56 print 'Stored procedure under test = usp_HiredAfter'
 57 print ' '
 58 print 'CaseID  Input           Expected Actual  Result'
 59 print '==============================================='
 60 
 61 open tCursor
 62 fetch next 
 63  from tCursor
 64  into @caseID, @input, @expectedChecksum
 65 
 66 while @@fetch_status = 0
 67 begin
 68 
 69  exec tap_Reset -- reset test bed data
 70 
 71  truncate table #resultRowset -- empty out the result rowset
 72 
 73  insert #resultRowset (empID, empLast, empDOH) -- call sp under test
 74   exec dbEmployees.dbo.usp_HiredAfter @input
 75 
 76  if (@@rowcount = 0)
 77   set @actualChecksum = 0
 78  else
 79   select @actualChecksum = checksum_agg(binary_checksum(*)) from #resultRowset
 80  
 81  if (@actualChecksum = @expectedChecksum)
 82   begin
 83    set @resultMsg = @caseID + '    ' + cast(@input as varchar(11)) + 
 84      ' ' + cast(@expectedChecksum as varchar(20)) + ' ' +
 85            cast(@actualChecksum as varchar(20)) + ' Pass'
 86    print @resultMsg
 87    insert into dbTestCasesAndResults.dbo.tblResults values(@caseID, 'Pass',
 88                                                            @whenRun)
 89   end
 90  else
 91   begin
 92    set @resultMsg = @caseID + '    ' + cast(@input as varchar(11)) + 
 93      ' ' + cast(@expectedChecksum as varchar(20)) + ' ' +
 94            cast(@actualChecksum as varchar(20)) + ' FAIL'
 95    print @resultMsg
 96    insert into dbTestCasesAndResults.dbo.tblResults values(@caseID, 'FAIL',
 97                                                            @whenRun)
 98   end
 99 
100  fetch next
101   from tCursor
102   into @caseID, @input, @expectedChecksum
103  
104 end
105 
106 close tCursor
107 deallocate tCursor
108 
109 drop table #resultRowset
110 
111 -- end script
View Code

當待測存儲過程返回out參數時,如何判斷測試結果是否通過

待測存儲過程腳本如下:

1 create procedure usp_GetLast
2  @empID char(3)
3  @empLast varchar(35) out
4 as
5  select @empLast =empLast from tblEmployees where empID = @empID
6  return @@rowcount
View Code

測試這個存儲過程的腳本如下:

 1 declare @input char(3)
 2 declare @empLat varchar(35)
 3 declare @retval int
 4 
 5 declare @expectedLast varchar(35)
 6 declare @expectedRet int
 7 
 8 set @input = 'e22'
 9 set @expectedLast = 'Baker'
10 set @expectedRet=1
11 
12 exec @retval =dbEmployees.dbo.usp_GetLast @input, @empLat out
13 if(@retval=@expectedRet and @empLat = @expectedLast)
14  print 'Pass'
15 else 
16  print 'Fail'
View Code

注解:

SQL存儲過程有一個常用的設計模式,即存儲過程可以通過參數返回一個或多個值。當存儲過程返回的值不是int類型時,這個模式是必須的,因為return關鍵字只接受int類型的變量。

當待測存儲過程沒有返回值時,如何判斷測試結果是否通過

待測存儲過程腳本如下:

1 create procedure usp_DeleteEmployee
2  @empID char(3)
3 as
4  delete from dbEmployees.dbo.tblEmployees where empID=@empID
View Code

測試這個存儲過程的腳本如下:

 1 declare @input char(3)
 2 
 3 declare @expected int
 4 declare @actual int
 5 
 6 set @input = 'e22'
 7 set @expected = 150847775
 8 
 9 exec dbEmployees.dbo.usp_DeleteEmployee @input
10 select @actual=checksum_agg(checksum(*)) from dbEmployees.dbo.tblEmployees
11 if(@actual=@expected)
12  print 'Pass'
13 else 
14  print 'Fail'
View Code

 

 

 

 

參考

[1] bcp命令詳解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM