如何(单元)测试数据密集型PL / SQL应用程序

时间:2010-04-19 14:19:28

标签: oracle unit-testing plsql tdd legacy

我们的团队愿意对在扩展现有庞大Oracle系统的正在运行的项目下编写的新代码进行单元测试。

系统仅用PL / SQL编写,由数千个表,数百个存储过程包组成,主要从表中获取数据和/或插入/更新其他数据。

我们的延期也不例外。大多数函数从一个非常复杂的SELECT语句返回数据,通过许多相互绑定的表(在返回它们之前稍加一些逻辑)或者从一个复杂的数据结构转换到另一个(以另一种方式复杂化)。

对此类代码进行单元测试的最佳方法是什么?

现有代码库没有单元测试。更糟糕的是,只有包,触发器和视图是源控制的,表结构(包括“alter table”内容和必要的数据转换是通过除版本控制之外的通道部署的)。在我们的项目范围内无法改变这一点。

维护测试数据似乎是不可能的,因为每周都有新的代码部署到生产环境中,通常没有事先通知,经常更改数据结构(在这里添加一个列,在那里删除一个)。

我很高兴有任何建议或参考来帮助我们。一些团队成员往往厌倦了弄清楚如何开始我们的经验,单元测试不包括PL / SQL数据密集型遗留系统(只有那些“来自本书”的绿地Java项目)。

4 个答案:

答案 0 :(得分:8)

有几种不同的PL / SQL测试工具。 Steven Feuerstein已经写了两个,utplsqlQuest Code Tester for Oracle(以前是QUTE)。我是utplsql的忠实粉丝,但它不再拥有一个活跃的支持社区(这是一种耻辱)。它也往往非常冗长,特别是在设置测试夹具时。它确实具有纯PL / SQL包的基本虚拟;源代码有点粗糙,但它是FOSS。

QCTO附带一个GUI,这意味着 - 像其他Quest产品,即TOAD - 它只是Windows。它并不能完全自动化测试数据生成,但它提供了一个支持它的接口。与其他Quest产品一样,QCTO虽然有免费软件,但仍获得许可。

史蒂文(披露,他是我的Oracle英雄之一)编写了所有PL / SQL测试工具的功能比较。显然,QOTC名列前茅,但我认为比较是诚实的。 Check it out.

关于utplsql中测试装置的建议

管理单元测试的测试数据可能是一个真正的痛苦。不幸的是,utplsql并没有提供太多的负担。所以

  • 始终针对已知值进行测试
    • 避免使用dbms_random;
    • 尝试将序列的使用限制为值无关紧要的列;
    • 日期也很棘手。避免使用硬编码日期:使用填充sysdate的变量。学会欣赏add_months()last_day()intervaltrunc(sysdate, 'MM')等。
  • 将测试数据与其他用户隔离。从头开始构建它。尽可能使用独特的价值观。
  • 只根据需要创建尽可能多的测试数据。容量测试是一个不同的责任。
  • 当测试更改数据的程序时,为每个单元测试创​​建特定记录。
  • 另外:不要依赖一次测试的成功输出来提供另一次测试的输入。
  • 在适当时测试单独测试之间简单报告数据共享记录的程序。
  • 尽可能共享框架数据(例如引用的主键)。
  • 使用自由文本字段(名称,描述,注释)来标识哪些测试使用该记录。
  • 尽量减少创建新记录所涉及的工作:
    • 仅分配测试套件所需的值和表的约束;
    • 尽可能使用默认值;
    • 尽可能程序化。

要记住的其他事项:

  • 设置测试夹具可能是一项耗时的练习。如果您有大量数据,请考虑构建一个过程来设置静态数据,每个会话可以运行一次,并且只包含ut_setup本身的易失性数据。在测试只读功能时,这尤其有用。
  • 请记住,创建测试数据本身就是一项编程练习,因而容易出错。
  • 使用utplsql的所有功能。在推断易变数据的值时,utAssert.EqQueryutAssert.EqQueryValueutAssert.EqTableutAssert.EqTabCountutAssert.Eq_RefC_Query都是非常有用的功能。
  • 在诊断测试运行时,如果没有按照我们期望的方式运行,那么获取所使用的数据会很有用。因此,请考虑使用空洞ut_teardown程序并在ut_setup开始时清除测试数据。

处理遗留代码

评论加里的帖子让我想起了另外一件你可能会觉得有用的事情。 Steven F将ulplsql写成了JUnit的PL / SQL实现,JUnit是Test First运动的Java先锋。然而,TDD的技术也可以应用于大量遗留代码(在此上下文中,遗留代码是没有任何单元测试的任何程序集)。

要记住的关键是你不必立即将所有东西都置于单元测试之下。逐步开始。为新内容构建单元测试Test First。在应用更改之前,对要更改的位进行单元测试,因此您知道在进行更改后它们仍然有效。

在这方面有很多想法,但(不可避免地,如果可耻的话)它主要来自OO程序员。 Michael Feathers是主要的一员。阅读他的文章Working Effectively With Legacy Code。如果你觉得它有用,他随后写了一本同名的书。

答案 1 :(得分:4)

采取以下方案

FUNCTION ret_count (local_client_id IN number) RETURN NUMBER IS
  v_ret number;
BEGIN
  SELECT count(*) INTO v_ret FROM table_1 WHERE client_id = local_client_id;
  RETURN v_ret;
END;

非常简单的功能,但有一堆东西可能出错。数据类型转换,索引,统计信息都可能影响查询路径,性能以及某些情况下的错误。还有很多松耦合,例如会话设置(例如语言偏好)。如果有人出现并向table_1添加了一列“LOCAL_CLIENT_ID”,则该函数的整个逻辑会发生变化。

我个人认为TDD不适合这种环境。由知识渊博的个人进行的代码审查将更有可能发现问题。

如果你有钱,那就看看Oracle的RAT(实际应用测试)进行回归测试。

答案 2 :(得分:3)

我使用DbFit对PL / SQL代码进行单元测试。试一试。

答案 3 :(得分:1)

utPLSQL可能会有所帮助,但听起来您需要更好的方法来维护测试数据。您可能还想查看Swingbench