Your Ad Here
首页 | 编程语言 | 网站建设 | 游戏天堂 | 冲浪宝典 | 网络安全 | 操作系统 | 软件时空 | 硬件指南 | 病毒相关 | IT 认证
软讯网络 > 冲浪宝典 > 网络资源 > SQL Server单元测试方法的尝试
【标  题】:SQL Server单元测试方法的尝试
【关键字】:SQL,Server
【来  源】:http://blog.csdn.net/lydmc/archive/2006/10/26/1352387.aspx

SQL Server单元测试方法的尝试

Your Ad Here

       我自己写过一些脚本,当时也写过测试脚本去验证,但是到了后面花在找这些脚本或者修改它们的时间也越来越多,感觉不够方便。
       后来接触了测试驱动开发(TDD),觉得这样似乎能够解决我的问题:

1.验证是自动进行的(不用人工判断数据的正确,因为代码里已经“记住了”怎么判断);

2.Setup保证了不用每次运行前都要修改脚本来适应当前环境;

3.Teardown保证了测试之间是独立的,没有运行顺序的要求。

主要思路:

T-SQL不是面向对象的语言, 但我们可以利用数据库的事务机制来模拟Setup和Teardown功能.

数据库的单元测试较麻烦的一点是结果集的比较, 这里给出一个存储过程(up_TableIsEqual)来处理.

 

/* 
Test case :模板

前置条件: 
*/

SET NOCOUNT ON
BEGIN TRAN
GO
--Setup Begin
    --相当于一个测试类的Setup函数, 可以把各个测试用例都会用到的初始化脚本放到这里
--
Setup End

GO
SAVE TRAN aa  --相当于测试类中的一个测试函数
GO    
    
--这里写每个函数不同的初始化脚本

    
--被测试模块 Begin
    SELECT A = 1 INTO TableA
    
--被测试模块 End
    
    
--验证    
    SELECT A = 1 INTO #Target-- 把预期的结果放到固定名称的临时表#Target中
    SELECT * INTO #Result FROM TableA -- 把处理的结果放到固定名称的临时表#Result中
    
    
EXEC up_ASSERT '测试函数1'  -- up_ASSERT过程里会比较#Result和#Target
GO
ROLLBACK TRAN aa  -- 回滚, 把这个"测试函数"的影响取消


GO
SAVE TRAN aa  --相当于测试类中的另一个测试函数
GO    
    
--这里写每个函数不同的初始化脚本

    
--被测试模块 Begin
    SELECT A = 2 INTO TableA
    
--被测试模块 End
    
    
--验证    
    SELECT A = 2 INTO #Target-- 把预期的结果放到固定名称的临时表#Target中
    SELECT * INTO #Result FROM TableA -- 把处理的结果放到固定名称的临时表#Result中
    
    
EXEC up_ASSERT '测试函数2'  -- up_ASSERT过程里会比较#Result和#Target
GO
ROLLBACK TRAN aa  -- 回滚, 把这个"测试函数"的影响取消

GO
--Teardown
ROLLBACK  -- 回滚, 把整个测试的影响都取消

--里面用到的过程

 

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[up_TableIsEqual]'and OBJECTPROPERTY(id, N'IsProcedure'= 1)
drop procedure [dbo].[up_TableIsEqual]
GO
SET NOCOUNT ON
GO

CREATE PROC up_TableIsEqual
/*结果集比较 liye
*/

@SourceTable varCHAR(100), 
@TargetTable varCHAR(100),
@IsDebug BIT = 0
--1 Equal
AS
DECLARE @Result INT

CREATE TABLE #SrcTbCol(
[name] sysname,
xtype 
tinyint
)


CREATE TABLE #TagTbCol(
[name] sysname,
xtype 
tinyint
)


SELECT Result = 0
INTO #ResultInTableisEqualProc


-- 比较表结构
IF SUBSTRING(@SourceTable11= '#'
BEGIN
 
INSERT INTO #SrcTbCol
 
SELECT [name], xtype 
 
FROM tempdb..syscolumns
 
WHERE [ID] = OBJECT_ID('tempdb..' + @SourceTable)
 
ORDER BY colid
END
ELSE BEGIN
 
INSERT INTO #SrcTbCol
 
SELECT [name], xtype 
 
FROM syscolumns
 
WHERE [ID] = OBJECT_ID(@SourceTable)
 
ORDER BY colid
END

IF SUBSTRING(@TargetTable11= '#'
BEGIN
 
INSERT INTO #TagTbCol
 
SELECT [name], xtype 
 
FROM tempdb..syscolumns
 
WHERE [ID] = OBJECT_ID('tempdb..' + @TargetTable)
 
ORDER BY colid
END
ELSE BEGIN
 
INSERT INTO #TagTbCol
 
SELECT [name], xtype 
 
FROM syscolumns
 
WHERE [ID] = OBJECT_ID(@TargetTable)
 
ORDER BY colid
END

IF (SELECT COUNT(*FROM #SrcTbCol) <> (SELECT COUNT(*FROM #SrcTbCol)
OR (SELECT COUNT(*FROM #SrcTbCol) 
<> (SELECT COUNT(*FROM #SrcTbCol a INNER JOIN #TagTbCol b ON a.[name] = b.[name])
BEGIN
  
RETURN 0
END

DECLARE @Prepare varCHAR(4000), @ColList varCHAR(1000)
--获取列的名称列表: A,B,C
BEGIN
 
SET @ColList = ''
 
 
SELECT @ColList = @ColList + [name] + ','
 
FROM #SrcTbCol
 
WHERE xtype NOT IN (343599)
 
 
SET @ColList = SUBSTRING(@ColList1LEN(@ColList- 1)
END

BEGIN  -- 生成新的临时表, 合并重复行, 和记录重复行的个数
 SET @Prepare = '
 SELECT _ColList_, CountForTableCompare = COUNT(*)
 INTO _SourceTb_2
 FROM _SourceTb_
 GROUP BY _ColList_
 
 SELECT _ColList_, CountForTableCompare = COUNT(*)
 INTO _TargetTb_2
 FROM _TargetTb_
 GROUP BY _ColList_
 
 
'
 
SET @Prepare = REPLACE(@Prepare'_ColList_'@ColList)
 
SET @Prepare = REPLACE(@Prepare'_SourceTb_'@SourceTable)
 
SET @Prepare = REPLACE(@Prepare'_TargetTb_'@TargetTable)
END

--加入重复行数列
INSERT INTO #SrcTbCol SELECT [name] = 'CountForTableCompare'56
INSERT INTO #TagTbCol SELECT [name] = 'CountForTableCompare'56

-- 比较结果数
DECLARE @s varCHAR(8000)
SET @s = 'UPDATE #ResultInTableisEqualProc
SET Result = 1 
WHERE (SELECT COUNT(*) FROM _SourceTb_2) = (SELECT COUNT(*) FROM _TargetTb_2)
AND (SELECT COUNT(*) FROM _SourceTb_2) = (SELECT COUNT(*) FROM _SourceTb_2 a INNER JOIN _TargetTb_2 b ON 
'

SELECT @s = @s + ' a.' + [name] + ' = b.' + [name] + ' AND'
FROM #SrcTbCol
WHERE xtype NOT IN (343599)

SET @s = REPLACE(@s'_SourceTb_'@SourceTable)
SET @s = REPLACE(@s'_TargetTb_'@TargetTable)
SET @s = SUBSTRING(@s1LEN(@s- 3)
SET @s = @s + ')'

IF @IsDebug = 1
BEGIN
 
PRINT @Prepare + @s
END

EXEC(@Prepare + @s)

SELECT @Result = Result FROM #ResultInTableisEqualProc

DROP TABLE #SrcTbCol
DROP TABLE #TagTbCol
RETURN @Result
GO


 

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[up_ASSERT]'and OBJECTPROPERTY(id, N'IsProcedure'= 1)
drop procedure [dbo].[up_ASSERT]
GO



CREATE PROC up_ASSERT
@ErrorMessage varCHAR(100= ''
AS
DECLARE @Res INT
EXEC @Res = up_TableIsEqual '#Target''#Result'  -- 比较结果和期望值



IF @Res <> 1
BEGIN
  
IF @ErrorMessage IS NULL
  
BEGIN
    
RAISERROR('Error'161)
  
END
  
ELSE BEGIN
    
RAISERROR(@ErrorMessage161)
  
END  
  
SELECT * FROM #Target
  
SELECT * FROM #Result
END




GO

 

问题:  

多连接时,锁的测试 (这个不太好办, 可能还得用程序来实现)

 结果集数据量很大时, (可以用基本表代替临时表保存预期的结果, 并建立索引以优化表连接的效率, 或者考虑用CHECKSUM_AGG来比较)

 限制:

 不能把事务回滚当作正常的处理

 

 这么写数据库脚本也挺有意思的,请大家多提意见.

 

SQL Server2005中的SMO编程:【上一篇】
Concept内容的翻译_1026(英语水平差,发出来主要是想让大家帮我指正一下,谢谢!):【下一篇】
【相关文章】
  • SQL Server2005中的SMO编程
  • SQL2005亲身体验
  • 用Ms SQL Server 存储过程操作DBF文件
  • MYSQL ODBC 乱码的解决方法
  • ADO.NET封装T-SQL存储过程操作类:ProcedureCommand
  • SQL -- 表的操作
  • 使用SQL*Loader向Oracle导入数据
  • ORACLE常用的SQL语法和数据对象
  • Apache Server的httpd.conf文件注释及配置指导
  • 查看apache,mysql进程
  • 【随机文章】
  • 监控某人邮件
  • PHP4之COOKIE支持详解
  • 在FLASH里怎样控制弹出窗口的大小
  • 真令人惊讶
  • 找出UNIX中最费CPU的进程
  • Displays the menu path for a transaction
  • 为Excel表格增加边框
  • Eclipse入门使用指南及开发Eclipse插件(5)
  • 英文单词频率统计工具
  • 一个家伙的辞职信
  • 【相关评论】
    没有相关评论
    【发表评论】
    姓名:
    邮件:
    随机码*
    评论*
          
    |  首 页  |  版权声明  |  联系我们   |  网站地图  |
    CopyRight © 2004-2007 bbb软讯网络 All Rigths Reserved.