我有一个使用BCP创建文件的存储过程。我需要使用预定义的预期结果集验证文件中的数据。有没有办法可以使用TSQLT进行测试?
答案 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返回。
所以,这里有三种可能性:
仅使用bcp.exe导出在tSQLt测试期间根本不修改的表中的预先存在的数据。 (这包括## Temp Tables。)
不要使用bcp.exe(,isql.exe,osql.exe,sqlcmd.exe等)来导出数据。更喜欢使用SQL CLR程序。正确编写(如给出@table参数),这些将在当前SQL连接中运行,因此不会被当前的ROW / PAGE / TABLE锁阻塞。
除了2之外,如果您不喜欢编写SQL CLR程序集,请尝试使用OLE自动化过程来导出数据。同样,这些将在当前的SQL连接中运行,因此,正确编写,不会被当前的ROW / PAGE / TABLE锁阻塞。 请注意,这会在SQL Server上打开更多安全漏洞,因为您需要通过sp_configure启用OLE自动化过程...但是如果您正在使用SQL中的bcp.exe,那么您已经在SQL上启用了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