测试对调用者的临时文件进行操作的存储过程 table
Testing a stored procedure that operates on a caller's temp table
我一直在尽最大努力遵循 http://www.sommarskog.se/share_data.html 和 tSQLt 文档中的智慧;试图让我的存储过程保持轻便和相对简单,以便它们很容易 testable。因此,我发现自己在主存储过程中创建了一个临时 table,然后在从主存储过程调用的 "secondary" 存储过程中对该临时 table 进行操作。这工作得很好,但事实证明测试起来有点尴尬。
单独测试 "secondary" 存储过程时,临时文件 table 必须已经存在。看起来在 [Set Up]
过程中创建临时 table 不会持续到单元测试,但创建完整的 table 会。
因此,为了避免在每个单元测试(及其完整的列定义)中重复 CREATE TABLE #temp
,我正在做以下变化:
EXEC tSQLt.NewTestClass 'SEtest';
GO
CREATE PROCEDURE [SEtest].[SetUp]
AS
BEGIN
CREATE TABLE SEtest.temptemplate (col1 int);
END;
GO
CREATE PROCEDURE [SEtest].[test example]
AS
BEGIN
-- Assemble
SELECT TOP (0) * INTO #temp FROM SEtest.temptemplate;
INSERT INTO #temp (col1)
VALUES (1),(2),(5),(7);
-- Act
EXEC dbo.REMOVE_EVEN_NUMBERS;
-- Assert
SELECT TOP (0) * INTO #expected FROM #temp;
INSERT INTO #expected (col1)
VALUES (1),(5),(7);
EXEC tSQLt.AssertEqualsTable '#expected', '#temp';
END;
GO
有没有更好的方法来协调 tSQLt 与通过 temp tables 在存储过程之间共享数据?
实在是没有更好的办法了
使用#temp table 作为 table 值参数引用在 T-SQL 中本质上是丑陋的,也会导致测试中的丑陋。但是,您的测试似乎经过深思熟虑,应该可以满足您的需求。
我建议对您的模式进行的唯一改进是在测试架构中永久创建 SEtest.temptemplate
table,而不是在每次测试运行时即时重新创建它。
使用SELECT ... INTO
创建特定于测试的 tables 是我在测试中到处使用的模式。由于您无法在子过程中创建临时 tables,因此这是我们唯一的干净选项。
无论哪种方式,都不要在测试中使用 CREATE TABLE
语句来创建 #temp table,因为如果 table 模式发生变化,这将成为维护的噩梦。
更新:
您可能要考虑另一种选择。由于您的#temp table 基本上是一个 table 值参数,因此使用 table 类型并不是一个坏主意。现在,这仍然很笨拙,但它提高了各方面的可维护性:
DECLARE @template AS dbo.tabletype;
SELECT * INTO #temptable FROM @template;
这使得可读性更清晰一些,特别是如果您的 table 类型的名称具有表现力(并且它与所讨论的过程处于相同的架构中)。
有了这个,您可以在任何调用过程中使用相同的模式,而不仅仅是在测试中。这也使实际代码多了一点 obvious/readable,如果 table 定义发生变化,它会减少维护工作。
我一直在尽最大努力遵循 http://www.sommarskog.se/share_data.html 和 tSQLt 文档中的智慧;试图让我的存储过程保持轻便和相对简单,以便它们很容易 testable。因此,我发现自己在主存储过程中创建了一个临时 table,然后在从主存储过程调用的 "secondary" 存储过程中对该临时 table 进行操作。这工作得很好,但事实证明测试起来有点尴尬。
单独测试 "secondary" 存储过程时,临时文件 table 必须已经存在。看起来在 [Set Up]
过程中创建临时 table 不会持续到单元测试,但创建完整的 table 会。
因此,为了避免在每个单元测试(及其完整的列定义)中重复 CREATE TABLE #temp
,我正在做以下变化:
EXEC tSQLt.NewTestClass 'SEtest';
GO
CREATE PROCEDURE [SEtest].[SetUp]
AS
BEGIN
CREATE TABLE SEtest.temptemplate (col1 int);
END;
GO
CREATE PROCEDURE [SEtest].[test example]
AS
BEGIN
-- Assemble
SELECT TOP (0) * INTO #temp FROM SEtest.temptemplate;
INSERT INTO #temp (col1)
VALUES (1),(2),(5),(7);
-- Act
EXEC dbo.REMOVE_EVEN_NUMBERS;
-- Assert
SELECT TOP (0) * INTO #expected FROM #temp;
INSERT INTO #expected (col1)
VALUES (1),(5),(7);
EXEC tSQLt.AssertEqualsTable '#expected', '#temp';
END;
GO
有没有更好的方法来协调 tSQLt 与通过 temp tables 在存储过程之间共享数据?
实在是没有更好的办法了
使用#temp table 作为 table 值参数引用在 T-SQL 中本质上是丑陋的,也会导致测试中的丑陋。但是,您的测试似乎经过深思熟虑,应该可以满足您的需求。
我建议对您的模式进行的唯一改进是在测试架构中永久创建 SEtest.temptemplate
table,而不是在每次测试运行时即时重新创建它。
使用SELECT ... INTO
创建特定于测试的 tables 是我在测试中到处使用的模式。由于您无法在子过程中创建临时 tables,因此这是我们唯一的干净选项。
无论哪种方式,都不要在测试中使用 CREATE TABLE
语句来创建 #temp table,因为如果 table 模式发生变化,这将成为维护的噩梦。
更新:
您可能要考虑另一种选择。由于您的#temp table 基本上是一个 table 值参数,因此使用 table 类型并不是一个坏主意。现在,这仍然很笨拙,但它提高了各方面的可维护性:
DECLARE @template AS dbo.tabletype;
SELECT * INTO #temptable FROM @template;
这使得可读性更清晰一些,特别是如果您的 table 类型的名称具有表现力(并且它与所讨论的过程处于相同的架构中)。
有了这个,您可以在任何调用过程中使用相同的模式,而不仅仅是在测试中。这也使实际代码多了一点 obvious/readable,如果 table 定义发生变化,它会减少维护工作。