首页 > Linux操作系统 > Linux操作系统 > utPLSQL单元测试入门指南
utPLSQL是一个用于Oracle PL/SQL的单元测试框架。
作者是Steven Feuerstein。
资源:
http://utplsql.sourceforge.net/
下载文件utplsql2.2.zip,解压后包含3个子目录:Code、Doc以及Examples。
在讨论细节之前,先了解相关术语。
针对某一单元或程序的测试。假设你已经创建了一个计算并返回指定时期范围内某产品销售总额的函数,名称为product.total_sales。然后你将创建一个过程用于测试该函数。
单元测试的单个实例或测试场景。你将会尝试不同的场景(有效或无效的产品ID,各种日期范围等等)。每个不同的输入参数值组合就是一个不同的测试用例。它们在一个单元测试程序内部打包执行。
测试单个PL/SQL包中所有程序(或者单个独立程序单元--过程或函数)功能的一组单元测试。按照当前utPLSQL工作的方式,你必须在一个测试包(即使是包含功能代码的包)中定义各种测试用例和单元测试。
一系列包测试。显然,任何高复杂度的应用都包含许多包,每一个都实现了不同的功能。一个测试套件包含了一系列能够依次执行的包,可以将其作为一个整体执行。
如果你使用的是Oracle8i或者更高版本,utPLSQL可以利用许多Oracle8i的特性,包括自治事务、调用者权限以及本地动态SQL。然而,utPLSQL仍然可以用于Oracle7(7.3.4及以上)以及Oracl8。使用utPLSQL具有以下要求:
如果你在同一个模式下安装并使用utPLSQL(例如,utPLSQL代码和表、被测试代码以及测试包位于同一模式中),不需要额外的权限。
然而,如果你在一个共享模式下安装utPLSQL并且从其他模式进行访问,你可能需要为utPLSQL模式授予额外的权限。utPLSQL使用动态PL/SQL运行测试代码。因此需要将这些代码元素(被测试代码与测试包)的EXECUTE权限直接授予utPLSQL模式,或者使用Oracle8i及以上的AUTHID CURRENT_USER功能。
注释:如果你已经安装了utPLSQL的早期版本,执行相同的步骤进行安装。安装程序在安装前不会删除任何对象,例如表。如果你想要安装一个全新的版本,而不是升级已有安装,请参照下文中的删除utPLSQL的步骤。
通过SQL*PLUS连接会话,当前用户将会拥有utPLSQL组件。如果还没有定义模式,必须先创建它。utPLSQL模式必须拥有以下权限:
connect system/manager create user utp identified by utp default tablespace users temporary tablespace temp; grant create session, create table, create procedure, create sequence, create view, create public synonym, drop public synonym to utp; alter user utp quota unlimited on users;
注释:如果该模式没有创建与删除公共同义词或者执行DBMS_PIPE包的权限,将会获得一些错误信息。但是utPLSQL仍然能够使用。
一旦连接到该模式,运行ut_i_do.sql文件,加上参数“install”,安装全部utPLSQL对象。你必须确认SQL*PLUS会话的工作目录与utPLSQL文件目录相同,然后提交以下命令:
SQL> @ut_i_do install
该文件将会创建所有的表、包以及其他所需的对象。注意,安装脚本将会使用SPOOL命令动态创建一些文件。因此,你需要拥有该目录下的写权限。
查看ut_i_do.log文件检验utPLSQL的安装。
要删除该产品,再次运行ut_i_do.sql脚本,加上参数“uninstall”,命令如下:
SQL> @ut_i_do uninstall
你可能想要测试一个独立的过程或函数、或者一个包中的一组程序。挑选程序并提出一组不同的用例。这些将会决定你运行的测试种类与数量。
例如,假设我已经创建了一个独立的函数betwnStr(一个SUBSTR函数的变形,基于开始位置与结束位置返回一个字串),存储在Examples目录的betwnstr.sf文件中。
CREATE OR REPLACE FUNCTION betwnstr (
string_in IN VARCHAR2,
start_in IN INTEGER,
end_in IN INTEGER
)
RETURN VARCHAR2
IS
l_start PLS_INTEGER := start_in;
BEGIN
IF l_start = 0
THEN
l_start := 1;
END IF;
RETURN (SUBSTR (string_in, l_start, end_in - l_start + 1));
END;
/
为了测试该函数,我将会使用各种不同的参数,如下表所示:
| 开始位置 | 结束位置 | 结果 |
| NULL | NOT NULL | NULL |
| NOT NULL | NULL | NULL |
| NULL | NULL | NULL |
| 3(正数) | 1(更小的正数) | NULL |
| 3(正数) | 100(大于字符串长度) | 从第3个位置开始的字串 |
utPLSQL提供了一个简单、自动化的方式运行测试。然而,要想自动化运行,必须遵从一些规则,以便utPLSQL能够找到并执行你的测试代码。这些规则包括:
测试代码必须位于一个测试包内。
测试包的规范应该以文件名ut_
测试包必须包含一个设置过程ut_setup以及一个清除过程ut_teardown,都没有任何参数。
测试包需要为每一个被测试程序编写一个单独的过程。
现在,你应该知道utPLSQL包含许多额外功能,允许你修改许多默认值(例如用于设置、清除以及测试过程的前缀)以及utPLSQL包的行为。此时先不进行详细讨论。
因此,如果我要测试这个独立的过程betwnstr,我的测试包规范将会如下所示,它存储在文件ut_betwnstr.pks中:
CREATE OR REPLACE PACKAGE ut_betwnstr
IS
PROCEDURE ut_setup;
PROCEDURE ut_teardown;
-- For each program to test...
PROCEDURE ut_BETWNSTR;
END ut_betwnstr;
/
现在,让我们创建包体,存储在文件ut_betwnstr.pkb中。在这个简单案例中,我不需要设置任何数据结构,而且不需要清除任何东西。我的清除程序可以为空(但是必须存在)。因此,前半部分为:
CREATE OR REPLACE PACKAGE BODY ut_betwnstr
IS
PROCEDURE ut_setup IS
BEGIN
NULL;
END;
PROCEDURE ut_teardown
IS
BEGIN
NULL;
END;接下来创建单元测试过程。我需要回顾测试用例表格并将它们转换为对utAssert包中程序的调用。
utAssert提供了许多“断言例程”来测试传入的值或表达式并记录结果。你可以使用它测试两个字符串、文件、表或者集合的相等性。你可以测试表达式结果是否为NULL。
对于每个测试用例,我提供了一个该用例的字符串描述,以及一个表达式。从“typical valid usaege”开始,我传入了一个字符串“abcdefg”,开始位置3以及结束位置5,而betwnstr应该返回“cde”。
PROCEDURE ut_betwnstr IS
BEGIN
utAssert.eq (
'Typical valid usage',
BETWNSTR(
STRING_IN => 'abcdefg'
,
START_IN => 3
,
END_IN => 5
),
'cde'
);
我调用了utAssert.eq,因为我想要比较betwnstr返回值与字符串“cde”。它们应该相等。
现在我可以编写其他用例的程序。
utAssert.isnull (
'NULL start',
BETWNSTR(
STRING_IN => 'abcdefg'
,
START_IN => NULL
,
END_IN => 5
)
);
utAssert.isnull (
'NULL end',
BETWNSTR(
STRING_IN => 'abcdefg'
,
START_IN => 2
,
END_IN => NULL
)
);
utAssert.isnull (
'End smaller than start',
BETWNSTR(
STRING_IN => 'abcdefg'
,
START_IN => 5
,
END_IN => 2
)
);
utAssert.eq (
'End larger than string length',
BETWNSTR(
STRING_IN => 'abcdefg'
,
START_IN => 3
,
END_IN => 200
),
'cdefg'
);
END ut_BETWNSTR;
END ut_betwnstr;
/
至此,已经创建了用于betwnstr函数的单元测试程序。编译这两个文件,确认没有编译错误。
SQL> @ut_betwnstr.pks Package created. SQL> @ut_betwnstr.pkb Package body created.
接下来就是运行测试了。
启动SQL*PLUS并以被测试代码拥有者的模式连接,也就是UTP。然后调用utPLSQL.test运行测试包。
SQL> exec utplsql.test ('betwnstr', recompile_in => FALSE)第二个参数,“recompile_in => FALSE”,说明已经编译了测试包,不需要重编译。
如果测试没有发现任何错误(断言程序没有检测到),将会看到以下输出:
SQL> exec utplsql.test ('betwnstr', recompile_in => FALSE)
SUCCESS: "betwnstr"如果测试检测到了失败,将会显示类似以下输出:
SQL> exec utplsql.test ('betwnstr', recompile_in => FALSE)
FAILURE: "betwnstr"
BETWNSTR: IS NULL: NULL start
BETWNSTR: End larger than string length; expected "cdeg", got "cdefg"至此,已经完成安装utPSQL,编写一个测试包并运行测试。
utPLSQL默认会尝试重编译你的测试包代码(包规范必须位于<name>.pks,包体必须位于
utConfig.autocompile(false);
如果想要使用该功能,utPLSQL需要使用UTL_FILE包读取源代码文件并编译这些代码。使用UTL_FILE前需要进行配置,参见下文。配置之后,必须调用utConfig.setdir通知utPLSQL测试包的存储位置。否则,utPLSQL将不能重编译测试包,而是显示一个错误信息。
假设将代码存储在E:\utplsql\testall中,使用以下调用:
SQL> exec utConfig.setdir ('e:\utplsql\testall')在2.1中描述了拥有组成utPLSQL框架的对象的用户。然而,总是存在一些关于哪个模式应该包含测试包以及连接哪个模式运行测试。可以使用不同的方式,但是最简单的方式如下:
要了解utPLSQL更多不同特性和功能,查看用户指南。
要想更深一步了家如何构建测试包,查看如何构建测试包。
更多构建测试用例与不同类型的测试包示例,查看示例文档。
如果想要utPLSQL自动重编译测试包,必须数据库启用了UTF_FILE(它允许读写操作系统文件)。数据库初始化参数文件必须至少包含一个utl_file_dir参数。
utl_file_dir =
也可以通过alter system命令设置spfile,修改该参数需要重启数据库实例。
访问不能递归到子目录。如果设置了以下参数:
utl_file_dir = c:\group\dev1 utl_file_dir = c:\group\prod\oe utl_file_dir = c:\group\prod\ar
不能使用UTF_FILE打开c:\group\prod\oe\reports目录中的文件。
不要在目录最后加上分隔符,如Unix中的斜线。以下设置将会导致读写文件时出现问题:
utl_file_dir = /tmp/orafiles/
使用以下代码(修改目录名和文件名之后)验证UTF_FILE能够正确读取文件。
SET SERVEROUTPUT ON
DECLARE
fid UTL_FILE.FILE_TYPE;
v VARCHAR2(32767);
PROCEDURE recNgo (str IN VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE ('UTL_FILE error ' || str);
UTL_FILE.FCLOSE (fid);
END;
BEGIN
/* Change the directory name to one to which you at least
|| THINK you have read/write access.
*/
fid := UTL_FILE.FOPEN ('e:\demo', 'existing_file', 'R');
UTL_FILE.GET_LINE (fid, v);
dbms_output.put_line (v);
UTL_FILE.FCLOSE (fid);
fid := UTL_FILE.FOPEN ('e:\demo', 'new_file', 'W');
UTL_FILE.PUT_LINE (fid, v);
UTL_FILE.FCLOSE (fid);
EXCEPTION
WHEN UTL_FILE.INVALID_PATH
THEN recNgo ('invalid_path');
WHEN UTL_FILE.INVALID_MODE
THEN recNgo ('invalid_mode');
WHEN UTL_FILE.INVALID_FILEHANDLE
THEN recNgo ('invalid_filehandle');
WHEN UTL_FILE.INVALID_OPERATION
THEN recNgo ('invalid_operation');
WHEN UTL_FILE.READ_ERROR
THEN recNgo ('read_error');
WHEN UTL_FILE.WRITE_ERROR
THEN recNgo ('write_error');
WHEN UTL_FILE.INTERNAL_ERROR
THEN recNgo ('internal_error');
END;
/
它会读取并显示E:\demo中existing_file文件中的第一行,然后将该行写入一个新建的文件new_file中。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/18870077/viewspace-1168052/ ,如需转载,请注明出处,否则将追究法律责任。