0

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

4

2 回答 2

2

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

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

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

a) 调用GetMyData- 使用“tSQLt.SpyProcedure”,您可以将其配置为返回单行用于导出的虚拟数据 - 但更重要的是用于确认该存储过程实际上已被调用

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' 生成的一个虚拟行

于 2014-10-01T20:24:31.693 回答
0

使用 tSQLt 测试来确认 bcp.exe 读取数据通常是可能的。

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

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

所以,这里有三种可能:

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

  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 Server 上启用了 xp_cmdshell。

要启用 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
于 2017-03-29T01:47:45.050 回答