代码之家  ›  专栏  ›  技术社区  ›  Dan Blair

测试存储过程的最佳方法是什么?

  •  21
  • Dan Blair  · 技术社区  · 16 年前

    像许多需要所有访问权限的公司一样,我们似乎在存储过程中锁定了许多业务逻辑。这些事情很简单,很难检验,其中一些已经变得愚蠢很久了。是否有人有一套最佳实践可以让自信地测试这些东西变得更容易?

    目前我们维护了30个左右的“问题”数据库。这并不总是有很好的文档记录,而且它肯定不是自动化的。

    8 回复  |  直到 9 年前
        1
  •  10
  •   Ben Hoffstein    16 年前

    一个同事发誓 TSQLUnit testing framework . 可能值得一看你的需求。

        2
  •  4
  •   Constantin    16 年前

    我们有一个非常薄的数据访问层,它基本上把存储过程设计成类似C方法的样子。然后,我们的nunit测试套件设置/拆卸以创建/回滚调用到DAL的事务和测试方法。没有什么比TSQLUnit测试套件更花哨的了,而且被证明更易于维护。

        3
  •  2
  •   Codewerks    16 年前

    不确定这是否是您要查找的,但是由于您使用的是SQL Server:我发现Linq是一个很好的工具测试存储过程。您只需将存储过程拖到DBML图上,然后作为DataContext上的方法调用它们。胜过为测试线束设置ADO连接等。例如,如果您在Visual Studio中设置了一个测试项目,您可以像在其他对象上测试方法一样简单地测试过程。如果您存储的过程返回结果集,我认为Linq会将其转换为匿名变量,您应该能够通过IEnumerable或iqueryable访问这些变量(请有人对此进行验证)。但是,如果您只返回返回代码,那么这应该是一种快速且相当简单的方法。

        4
  •  2
  •   Ladi    12 年前

    试试TST。您可以从以下位置下载并安装: http://tst.codeplex.com/

        5
  •  2
  •   benilov Ed Harper    12 年前

    我注意到你的文章被标记为sqlserver。如果是这样,那么您应该看看作为Visual Studio一部分的数据库专业人员的TeamEdition。以下是一些文章:

    最后一个实际上是跨数据库平台,而DBPro目前仅是SQL服务器。

        6
  •  1
  •   MattMcKnight    16 年前

    这似乎是一个糟糕的政策。也许您可以编写一个执行SQL的存储过程,并开始将代码转换为在其中运行。

    无论如何,我将通过传统的自动化框架测试调用存储过程。作为应用程序和数据之间的网关,这些应该作为集成测试处理,而不是纯单元测试。但是,您可以使用基于XUnit的单元测试框架来驱动它们。只要您的测试有权对数据库运行SQL,或者通过我前面提到的方法,您就应该能够断言所做的更改是正确的。

    一个挑战是你指出他们变得越来越冗长。我建议将它们分解成子例程,并使它们尽可能小。它使测试更容易,维护也更容易。

        7
  •  1
  •   6eorge Jetson    16 年前

    这是我的低技术、快速的方法,只需将示例输入方便地保存在DDL中。

    
    USE [SpacelySprockets]
    
    GO
    /****** Object: StoredProcedure [dbo].[uspBrownNoseMrSpacely] Script Date: 02/03/3000 00:24:41 ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    --================================
    --Stored Procedure DDL:
    --================================ --Example Inputs
    /*
    DECLARE @SuckupPloyId int
    DECLARE @SuckupIdentityRecordId int
    SET @SuckupPloyId = 3
    */ -- =============================================
    -- Author: 6eorge Jetson
    -- Create date: 01/02/3000
    -- Description: Sucks up to the boss
    -- =============================================
    CREATE PROCEDURE [dbo].[uspBrownNoseMrSpacely]
    @SuckupPloyId int
    ,@SuckupIdentityRecordId int OUTPUT
    AS
    BEGIN DECLARE @EmployeeId int DECLARE @SuckupPoints int DECLARE @DateTimeStamp datetime SET @EmployeeId = dbo.svfGetEmployeeId('6eorge Jetson') SET @SuckupPoints = dbo.svfGetSuckupPoints(@SuckupPloyId) SET @DateTimeStamp = getdate() --Data state-changing statement in sproc INSERT INTO [dbo].[tblSuckupPointsEarned]([EmployeeId], [SuckupPoints], [DateTimeStamp] ) VALUES (@EmployeeId, @SuckupPoints, @DateTimeStamp) SET @SuckupIdentityRecordId = @@Identity END
    --Unit Test Evidence Display /* SELECT @EmployeeId as EmployeeId ,@SuckupPoints as SuckupPoints ,@DateTimeStamp as DateTimeStamp */ --========================================================================== --After editing for low-tech, non-state changing "unit-like" test invocation --========================================================================== --Example Inputs DECLARE @SuckupPloyId int DECLARE @SuckupIdentityRecordId int SET @SuckupPloyId = 3 /* -- ============================================= -- Author: 6eorge Jetson -- Create date: 01/02/3000 -- Description: Sucks up to the boss -- ============================================= CREATE PROCEDURE [dbo].[uspBrownNoseMrSpacely] @SuckupPloyId int ,@SuckupIdentityRecordId int OUTPUT AS BEGIN */ DECLARE @EmployeeId int DECLARE @SuckupPoints int DECLARE @DateTimeStamp datetime SET @EmployeeId = dbo.svfGetEmployeeId('6eorge Jetson') SET @SuckupPoints = dbo.svfGetSuckupPoints(@SuckupPloyId) SET @DateTimeStamp = getdate() --Data state-changing statement now commented out to prevent data state change -- INSERT INTO [dbo].[tblSuckupPointsEarned]([EmployeeId], [SuckupPoints], [DateTimeStamp] ) -- VALUES (@EmployeeId, @SuckupPoints, @DateTimeStamp) SET @SuckupIdentityRecordId = @@Identity --END --Need to comment out the sproc "END" also --Unit Test Evidence Display SELECT @EmployeeId as EmployeeId ,@SuckupPoints as SuckupPoints ,@DateTimeStamp as DateTimeStamp

    它对UDF更有效,因为没有需要担心的状态变化。 显然,我不建议用它代替测试框架, 但如果我坚持这一简单的秒成本原则

    断言我的可管理大小的存储过程至少通过了一个简单的“单元测试”

    在执行创建过程之前,我发现我犯的错误更少(可能是因为纪律性而不是测试本身)。

        8
  •  1
  •   Matthew Farwell    16 年前

    我使用的一种方法是编写一个“临时”单元测试来重构特定的存储过程。您可以保存来自数据库的一组查询的数据,并将它们存储在单元测试可以访问它们的地方。

    然后,重构您的过程库存。返回的数据应该相同,可以直接与保存的数据进行自动或手动比较。

    另一种方法是并行运行两个存储过程,并比较结果集。

    这对于只选择存储过程尤其有效,但更新、插入和删除更为复杂。

    我使用这个方法使代码达到一种状态,在这种状态下它更容易受到单元测试的影响,或者更简单,或者两者兼而有之。