【问题标题】:How to apply an index to a faked table in tSQLt如何将索引应用于 tSQLt 中的伪造表
【发布时间】:2014-07-16 17:21:07
【问题描述】:

使用适用于 MS SQL Server (2012) 的优秀 tSQLt 测试框架 (v1.0.5137.39257),我正在尝试构建一个测试来检查 UNIQUE INDEX 是否有效,并在重复值出现时生成异常插入。

经过大量搜索,我找不到将索引应用于已伪造的表(内置 tSQLt proc 或额外代码)的方法。需要像tSQLt.ApplyIndex 这样的东西。有没有人设法做到这一点?

另一种可能性是分叉 tSQLt 代码并基于 http://gallery.technet.microsoft.com/scriptcenter/SQL-Server-Generate-Index-fa790441 处的代码添加一个 proc 以在伪造的表上重新创建索引。然而,这将是相当多的工作......

测试条件(假设数据库中已经安装了tSQLt):

-- Create a sample table with a UNIQUE INDEX
SET ANSI_NULLS, QUOTED_IDENTIFIER, ANSI_PADDING ON
GO
CREATE TABLE dbo.tblTestUniqueIndex (
  id INT NOT NULL IDENTITY (1, 1),  TheField varchar(50) NOT NULL,
  CONSTRAINT PK_tblTestUniqueIndex PRIMARY KEY CLUSTERED (id ASC)
) ON [PRIMARY];
GO
CREATE UNIQUE NONCLUSTERED INDEX UX_TestUniqueIndex ON dbo.tblTestUniqueIndex
(TheField ASC)
ON [PRIMARY];
GO

创建测试类,测试并运行(当然失败是因为过程调用ApplyIndex不存在):

EXEC tSQLt.NewTestClass 'tests';
GO
CREATE PROCEDURE tests.[test that inserting a duplicate value in tblTestUniqueIndex raises an error]
AS
BEGIN
  EXEC tSQLt.FakeTable @TableName='dbo.tblTestUniqueIndex';

  -- WE NEED SOMETHING LIKE THIS
  --EXEC tSQLt.ApplyIndex @TableName='dbo.tblTestUniqueIndex', @ConstraintName='UX_TestUniqueIndex' 

  EXEC tSQLt.ExpectException;

  INSERT dbo.tblTestUniqueIndex (TheField) VALUES ('Cape Town');
  INSERT dbo.tblTestUniqueIndex (TheField) VALUES ('Cape Town');

END;
GO
EXEC tSQLt.Run 'tests.[test that inserting a duplicate value in tblTestUniqueIndex raises an error]'
GO

当然,如果索引不工作,上述测试就会失败。

清理:

DROP PROCEDURE tests.[test that inserting a duplicate value in tblTestUniqueIndex raises an error]
GO
EXEC tSQLt.DropClass 'tests'
GO
DROP TABLE dbo.tblTestUniqueIndex
GO

谢谢

【问题讨论】:

  • 我承认我没有使用过 TSQLT 但正在验证唯一索引是否可以防止重复条目真的是一个有效的测试吗?如果它不起作用,你会怎么做?似乎框架功能不在单元测试的范围内。
  • 你能应用约束吗?
  • 因此可以禁用索引,或者可以通过其他方式强制执行唯一性。在我们将架构同步到生产服务器之前,单元测试需要验证无论以何种方式插入重复值都会引发错误。

标签: sql sql-server unit-testing tsqlt


【解决方案1】:

你不能创建一个唯一的约束吗?您仍然会在后台获得所需的索引。 tSQLt.ApplyConstraint 对唯一键的作用与对主键的作用相同——但仅在最新版本的 IIRC 中。例如:

从您在上面创建的表的类似版本开始,为每种类型的唯一约束提供足够的列

-- Create a sample table with a UNIQUE INDEX
set ansi_nulls, quoted_identifier, ansi_padding on
go

if object_id('dbo.StackTable') is not null
    drop table dbo.StackTable;

create table dbo.StackTable
(
  Id int not null identity(1, 1)
, UniqueKeyColumn varchar(50) null
, UniqueIndexColumn int null
);
go

if object_id('PK_StackTable') is null
    alter table dbo.StackTable add constraint [PK_StackTable]
        primary key clustered (Id);
go

if object_id('AK_StackTable_UniqueKeyColumn') is null
    alter table dbo.StackTable add constraint [AK_StackTable_UniqueKeyColumn]
        unique nonclustered  (UniqueKeyColumn);
go

if object_id('NCI_StackTable_UniqueIndexColumn') is null
    create unique nonclustered index [NCI_StackTable_UniqueIndexColumn]
        on dbo.StackTable (UniqueIndexColumn);
go

新建一个测试类(假设已经安装了最新版本1.0.5325.27056)

if schema_id('StackTableTests') is null
    exec tSQLt.NewTestClass @ClassName = 'StackTableTests';
go

第一个测试确认 Id 被限制为唯一且该约束是主键

if object_id('[StackTableTests].[test id is unique]') is not null
    drop procedure [StackTableTests].[test id is unique];
go

create procedure [StackTableTests].[test id is unique]
as
begin
    exec tSQLt.FakeTable @TableName = 'dbo.StackTable';
    exec tSQLt.ApplyConstraint @TableName = 'dbo.StackTable', @ConstraintName = 'PK_StackTable';

    --! Add the row we're going to duplicate
    insert dbo.StackTable (Id) values (-999);

    --! If we insert the same value again, we should expect to see an exception
    exec tSQLt.ExpectException @ExpectedErrorNumber = 2627
        , @ExpectedMessagePattern = 'Violation of PRIMARY KEY constraint%';

    insert dbo.StackTable (Id) values (-999);
end
go

下一个测试还使用ApplyConstraint 来确认UniqueKeyColumn 也使用唯一约束被约束为唯一

if object_id('[StackTableTests].[test UniqueKeyColumn is unique]') is not null
    drop procedure [StackTableTests].[test UniqueKeyColumn is unique];
go

create procedure [StackTableTests].[test UniqueKeyColumn is unique]
as
begin
    exec tSQLt.FakeTable @TableName = 'dbo.StackTable';
    exec tSQLt.ApplyConstraint
        @TableName = 'dbo.StackTable', @ConstraintName = 'AK_StackTable_UniqueKeyColumn';

    --! Add the row we're going to duplicate
    insert dbo.StackTable (UniqueKeyColumn) values ('Oops!');

    --! If we insert the same value again, we should expect to see an exception
    exec tSQLt.ExpectException @ExpectedErrorNumber = 2627
        , @ExpectedMessagePattern = 'Violation of UNIQUE KEY constraint%';

    insert dbo.StackTable (UniqueKeyColumn) values ('Oops!');
end
go

目前,测试唯一索引的唯一方法是针对真实的、非伪造的表。在此示例中,UniqueKeyColumnvarchar(50),因为这是真实表,它可能已经包含数据。所以我们需要能够指定两个唯一的值,这样我们的测试就不会破坏错误的约束。最简单的方法是使用几个 GUID。

if object_id('[StackTableTests].[test UniqueIndexColumn is unique]') is not null
    drop procedure [StackTableTests].[test UniqueIndexColumn is unique];
go

create procedure [StackTableTests].[test UniqueIndexColumn is unique]
as
begin
    --! Have to use the real table here as we can't use ApplyConstraint on a unique index
    declare @FirstUniqueString varchar(50) = cast(newid() as varchar(50));
    declare @NextUniqueString varchar(50) = cast(newid() as varchar(50));

    --! Add the row we're going to duplicate
    insert dbo.StackTable (UniqueKeyColumn, UniqueIndexColumn) values (@FirstUniqueString, -999);

    --! If we insert the same value again, we should expect to see an exception
    exec tSQLt.ExpectException @ExpectedErrorNumber = 2601
        , @ExpectedMessagePattern = 'Cannot insert duplicate key row in object ''dbo.StackTable'' with unique index%';

    insert dbo.StackTable (UniqueKeyColumn, UniqueIndexColumn) values (@NextUniqueString, -999);
end
go

exec tSQLt.Run '[StackTableTests]';
go

针对真实表进行测试的潜在问题是它可能具有对其他表的外键引用,而外键引用又可能对其他表具有其他 FK 引用等等。没有简单的方法可以解决这个问题,但我已经实现了测试数据构建器模式的一个版本。这背后的想法是每个实体的存储过程,它自动处理这些依赖关系(即,将任何必要的行添加到父表和祖父表)。通常,在创建了所有依赖项后,TDB 存储过程会将生成的 ID 用作输出参数,以便可以重用这些值。

因此,如果我使用的是测试数据构建器模式,我的测试可能如下所示:

create procedure [StackTableTests].[test UniqueIndexColumn is unique]
as
begin
    --! Have to use the real table here as we can't use ApplyConstraint on a unique index
    declare @FirstUniqueString varchar(50) = cast(newid() as varchar(50));
    declare @NextUniqueString varchar(50) = cast(newid() as varchar(50));
    declare @NewId int;

    --! Add the row we're going to duplicate
    -- The TDB should output the ID's of any FK references so they
    --! can be reused on the next insert
    exec TestDataBuilders.StackTableBuilder
              @UniqueKeyColumn = @FirstUniqueString
            , @UniqueIndexColumn = -999

    --! If we insert the same value again, we should expect to see an exception
    exec tSQLt.ExpectException @ExpectedErrorNumber = 2601
        , @ExpectedMessagePattern = 'Cannot insert duplicate key row in object ''dbo.StackTable'' with unique index%';   

    insert dbo.StackTable (UniqueKeyColumn, UniqueIndexColumn) values (@NextUniqueString, -999);
end
go

exec tSQLt.Run '[StackTableTests]';
go

几年前,我写了一篇关于在 SQL 中使用Test Data Builder pattern 的博客文章。我希望这有助于解释我的想法。

【讨论】:

  • +1 感谢您的详细回答!!我知道我没有最新版本的 tSQLt - 仅此一项就值得标记为答案和 +1
【解决方案2】:

首先,我必须承认我喜欢你在示例中选择的城市。我不明白你为什么在这个例子中使用 FakeTable。为什么不直接编写没有 FakeTable 的测试并针对真实表进行测试。在某些情况下,这可能会有点痛苦。即,如果您有一个包含许多必填字段的表,但实际上可能有更好的方法来编写您想要的测试。如果您通过插入重复行来检查唯一约束或主键,我建议您测试 SQL Server 本身的功能是否按预期工作。如果我要编写测试,我会通过查询 information_schema 或 sys 表来测试约束是否存在。

【讨论】:

    【解决方案3】:
    CREATE TABLE TestTable (
            FirstName VARCHAR(255),
            LastName VARCHAR (255),
            Gender VARCHAR (255),
            Age INT
        );
    
    INSERT INTO TestTable (FirstName, LastName, Gender, Age) VALUES ('Tapos','Noor','Female',21);
    
    ALTER TABLE TestTable ADD CONSTRAINT My_Constraint UNIQUE (FirstName);
    
    EXEC tSQLt.FakeTable @TableName = 'TestTable';
    EXEC tSQLt.ApplyConstraint @TableName = 'TestTable', @ConstraintName = 'My_Constraint';
    
    INSERT INTO TestTable (FirstName, LastName, Gender, Age) VALUES ('Tapos', 'Noor', 'Male',24);
    
    EXEC tSQLt.ExpectException @ExpectedErrorNumber = 2627
            , @ExpectedMessagePattern = 'Violation of UNIQUE KEY constraint%';
    
    INSERT INTO TestTable (FirstName, LastName, Gender, Age) VALUES ('Narullah', 'Noor', 'Male',24);
    

    【讨论】:

    • 这是一个基本示例,但希望对您有所帮助
    • 为什么投反对票?这是最接近的答案 - 谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-13
    • 2021-10-30
    • 2016-02-11
    • 2010-09-21
    • 2012-07-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多