tsqlt方法测试生成文件作为输出的存储过程

时间:2014-07-22 18:31:52

标签: sql-server tsqlt

我有一个使用BCP创建文件的存储过程。我需要使用预定义的预期结果集验证文件中的数据。有没有办法可以使用TSQLT进行测试?

2 个答案:

答案 0 :(得分:2)

另一种选择可能是分担责任。一个函数或存储过程用于生成所需结果,另一个用于调用第一个函数或导出数据。

您可以编写一个或多个测试来验证GetMyData过程是否正在构建正确的结果。

然后,您需要针对ExportMyData存储过程编写测试以证明它:

a)调用GetMyData - 使用`tSQLt.SpyProcedure'您可以配置为返回单行虚拟数据以进行导出 - 但更重要的是用于确认实际调用此sproc

b)实际创建一个文件 - 你可以在测试结束时做这样的事情:

declare @tbl table (FileExists int, IsDirectory int, ParentDirectoryExists int)

insert @tbl
exec xp_fileexist 'c:\temp\greg.txt';

declare @expected int = 1;
declare @actual int = (select FileExists from @tbl)

exec tSQLt.AssertEquals @expected, @actual, 'Expected file "c:\temp\greg.txt" not found';

这不允许你做的唯一事情是测试GetMyData的输出实际上是否写入了生成的文件。为此,如前所述,您可能需要使用BULK INSERT。至少如果按照我的建议划分责任,可以使用更常规的方法来测试实际输出,而BULK INSERT测试只需要验证`tSQLt.SpyProcedure'

生成的一个虚拟行。

答案 1 :(得分:0)

使用tSQLt测试通常可以确认bcp.exe读取数据。

另一方面,使用tSQLt测试确认bcp.exe写出数据通常很成问题。

原因是tSQLt测试在SQL Transactions中运行,因此他们可以清理您在每次测试结束时所做的任何修改。这基本上排除了任何涉及外部进程(bcp.exe,isql.exe,osql.exe,sqlcmd.exe等)的tSQLt测试,访问您的tSQLt测试(或安装程序)修改的任何表,因为这些表将具有ROW /在当前tSQLt测试期间,PAGE / TABLE锁定在它们上面。外部进程将使用不同的SQL连接重新连接回数据库,因此将被tSQLt Transaction保存的ROW / PAGE / TABLE锁阻塞。特别是,bcp.exe将永远等待释放锁,这将永远不会发生,因为tSQLt测试正在等待bcp.exe返回。

所以,这里有三种可能性:

  1. 仅使用bcp.exe导出在tSQLt测试期间根本不修改的表中的预先存在的数据。 (这包括## Temp Tables。)

  2. 不要使用bcp.exe(,isql.exe,osql.exe,sqlcmd.exe等)来导出数据。更喜欢使用SQL CLR程序。正确编写(如给出@table参数),这些将在当前SQL连接中运行,因此不会被当前的ROW / PAGE / TABLE锁阻塞。

  3. 除了2之外,如果您不喜欢编写SQL CLR程序集,请尝试使用OLE自动化过程来导出数据。同样,这些将在当前的SQL连接中运行,因此,正确编写,不会被当前的ROW / PAGE / TABLE锁阻塞。 请注意,这会在SQL Server上打开更多安全漏洞,因为您需要通过sp_configure启用OLE自动化过程...但是如果您正在使用SQL中的bcp.exe,那么您已经在SQL上启用了xp_cmdshell服务器

  4. 要启用OLE自动化过程,请执行以下操作:

    sp_configure 'Show Advanced Options', 1;
    GO
    RECONFIGURE;
    GO
    sp_configure 'Ole Automation Procedures', 1;
    GO
    RECONFIGURE;
    GO
    sp_configure 'Show Advanced Options', 0;
    GO
    RECONFIGURE;
    GO
    

    将XML变量写入文件的SQL存储过程可能如下所示:

    create procedure [dbo].[sp_ExportXmlDataToFile]
        @XmlData xml,
        @XmlFilename nvarchar(255)
    as begin
        declare @OleAutomationObjectHandle int, @FileHandle int;
        declare @HResult int, @Source nvarchar(255), @Description nvarchar(255);
    
        --REF: OpenTextFile Method, https://msdn.microsoft.com/en-us/library/aa265347(v=vs.60).aspx
        declare @IOMode_ForReading int = 1, @IOMode_ForWriting int = 2, @IOMode_ForAppending int = 8;
        declare @Create_OpenExisting int = 0, @Create_CreateNew int = 1;
        declare @FileFormat_ASCII int = 0, @FileFormat_Unicode int = -1, @FileFormat_Default int = -2; --<<--NOTE: Negative numbers
    
        execute @HResult = sys.sp_OACreate N'Scripting.FileSystemObject', @OleAutomationObjectHandle out
        if (@HResult <> 0)
        begin
            exec sys.sp_OAGetErrorInfo @OleAutomationObjectHandle, @Source out, @Description out
            raiserror(N'Error Creating COM Component (Scripting.FileSystemObject) 0x%x, %s, %s', 16, 1, @HResult, @Source, @Description) 
        end
        else
        begin
            execute @HResult = sys.sp_OAMethod @OleAutomationObjectHandle, N'OpenTextFile', @FileHandle out, @XmlFilename, @IOMode_ForWriting, @Create_CreateNew, @FileFormat_Default
            if (@HResult <> 0)
            begin
                exec sys.sp_OAGetErrorInfo @OleAutomationObjectHandle, @Source out, @Description out
                raiserror(N'Error calling COM Component (Scripting.FileSystemObject.OpenTextFile) 0x%x, %s, %s', 16, 1, @HResult, @Source, @Description) 
            end
            else
            begin
                declare @Text nvarchar(max) = cast(@XmlData as nvarchar(max))
                execute sys.sp_OAMethod @FileHandle, N'Write', null, @Text
                execute sys.sp_OADestroy @FileHandle
            end
            execute sys.sp_OADestroy @OleAutomationObjectHandle
        end
    end
    go
    

    然后,使用它的tSQLt测试看起来像这样:

    create procedure [SomethingOrOther_Tests].[Test That sp_LoadXmlFile Inserts Data]
    as begin
        -- Assemble
        declare @Expected uniqueidentifier = newid();
        declare @XmlFilename nvarchar(255) = N'C:\Temp\SomethingOrOther_Tests.Test That sp_LoadXmlFile Inserts Data.xml';
        declare @XmlData xml =
    N'<S11:Envelope xmlns:S11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <S11:Header/>
        <S11:Body/>
    </S11:Envelope>';
        exec [dbo].[sp_ExportXmlDataToFile] @XmlData=@XmlData, @XmlFilename=@XmlFilename
    
        -- Act
        exec [dbo].[sp_LoadXmlFile] @ImportGUID=@Expected, @XmlFilename=@XmlFilename
    
        -- Assert
        declare @Actual uniqueidentifier = (select ImportGUID from dbo.tImportTable where ImportGUID=@Expected)
        exec [tSQLt].[AssertEquals] @Expected, @Actual, 'Did not find expected ImportGUID'
    end
    go