2014-07-16 16 views
0

对MS SQL Server(2012)使用优秀的tSQLt测试框架(v1.0.5137.39257),我试图构建一个测试来检查UNIQUE INDEX的工作原理并在插入重复值时生成异常。如何在tSQLt中对伪造表应用索引

经过广泛的搜索后,我无法找到一种方法将索引应用于伪造的表(内置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 

感谢

+2

我承认我没有使用TSQLT,但正在验证一个唯一索引可以防止重复条目真的是一个有效的测试吗?如果它不起作用,你会做什么改变?似乎框架功能不在单元测试的范围内。 –

+0

你可以应用约束吗? – Andrew

+0

因此索引可以被禁用,或者唯一性可以通过一些其他手段来强制执行。在我们将模式同步到生产服务器之前,单元测试需要验证,无论如何,插入重复值都会引发错误。 – DaveBoltman

回答

3

难道你不能创建一个唯一的约束吗?你仍然可以得到所需的索引。 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引用其他表的外键引用。有没有简单的方法来处理这个问题,但我已经实现了Test Data Builder模式的一个版本。这背后的想法是每个实体自动处理这些依赖关系的存储过程(即向父级表父母&添加任何必要的行)。通常,创建所有依赖项后,TDB sproc会将生成的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 

我写了一对夫妇几年前使用Test Data Builder pattern用于SQL博客文章。我希望这有助于解释我的想法。

+0

+1谢谢您的详细解答!我看到我没有tSQLt的最新版本 - 仅此一项就值得作为答案+1 +1 – DaveBoltman

0

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

-1
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); 
+0

这是一个基本的例子,但希望它有帮助 –