14

faketable 函数没有重新分配为正常。我使用 faketable 的所有表现在都包含我在插入单元测试时使用的值的内容。它有很多表,它使我的数据库毫无用处。请帮助解决此问题或至少解决其原因。这让我对在我们的 CI 部署过程中使用它感到非常紧张,也许更重要的是在我们的本地开发工作中。

4

5 回答 5

7

您的某个测试或您的代码可能使事务处于无法回滚的状态。这通常会导致在结果中看到一个或多个带有“错误”(而不是“成功”或“失败”)的测试。

在这些情况下,FakeTable 操作不会回滚,并且表仍处于伪造状态。

在幕后,FakeTable 重命名表并创建它的新副本。发生重命名时,操作会记录在 tSQLt.Private_RenamedObjectLog 中。

例如,您可以使用以下代码重现 tSQLt 无法正常回滚的错误:

EXEC tSQLt.NewTestClass 'SOF_Example'
GO

CREATE TABLE SOF_Example.MyTable (i INT);
GO

INSERT INTO SOF_Example.MyTable (i) VALUES (5);
GO

CREATE PROCEDURE SOF_Example.[test fake a table]
AS
BEGIN
    EXEC tSQLt.FakeTable 'SOF_Example.MyTable';

    INSERT INTO SOF_Example.MyTable (i) VALUES (12);

    COMMIT;
END;
GO

EXEC tSQLt.Run 'SOF_Example';

您可以使用此代码查看重命名的表日志:

SELECT OriginalName, SCHEMA_NAME(schema_id) + '.' + name AS [Name of Renamed Table], create_date
FROM tSQLt.Private_RenamedObjectLog
JOIN sys.objects ON ObjectId = object_id;

如果您已多次重新执行测试,则每个伪造表的日志中可能有许多条目。您可以使用 create_date 来帮助确定哪一个包含原始数据。

现在,说了这么多:最好不要在必须保存数据的数据库中编写和执行测试用例。最好的方法是使用不包含用户数据(最多只包含基本配置数据)的数据库。您应该在空白数据库中进行开发和单元测试。填充的数据库应该用于其他形式的测试,例如集成、可用性、性能等。

于 2012-08-30T18:39:59.927 回答
6

这仅处理将表放回原位(因为这是我和 OP 都遇到的问题),但使用删除表的行可能会使其与其他对象类型一起使用。

DECLARE @cmd nvarchar(MAX) = '';

WITH x AS (
    SELECT TOP 10000 
           PL.Id                                            AS Id
          ,PARSENAME(PL.OriginalName,1)                     AS OriginalName
          ,ISNULL(SO.name,'')                               AS name
          ,QUOTENAME(SCHEMA_NAME(ISNULL(SO.schema_id,1)))   AS SchemaName
          ,ISNULL(SEP.major_id,-1)                          AS major_id
      FROM tSQLt.Private_RenamedObjectLog PL
      LEFT JOIN sys.objects SO
        ON ObjectId = object_id
      LEFT JOIN sys.extended_properties SEP
        ON SEP.major_id = SO.object_id
       AND SEP.name = 'tSQLt.FakeTable_OrgTableName'
     ORDER BY SO.create_date DESC
)
SELECT @cmd = @cmd 
       + CASE WHEN x.name = '' OR OriginalName = x.name 
              THEN N'DELETE tSQLt.Private_RenamedObjectLog WHERE Id = ' + CAST(x.Id AS nvarchar) + N';'
              ELSE N'DROP ' 
                 + N'TABLE'   --Replace this with a CASE statement to deal with other object types
                 + N' ' + SchemaName + '.' + QUOTENAME(x.OriginalName) + '; ' 
                 + NCHAR(13) + NCHAR(10) + N'EXEC sp_rename ''' + SchemaName + N'.' 
                                         + QUOTENAME(x.name) + N''',''' + OriginalName + N''';'
                 + NCHAR(13) + NCHAR(10) + N'IF OBJECT_ID('''+SchemaName + N'.' + QUOTENAME(x.name)+N''') IS NULL'
                 + NCHAR(13) + NCHAR(10) + N'BEGIN'
                 + CASE WHEN x.major_id != -1 
                        THEN NCHAR(13) + NCHAR(10) + N'    EXEC sp_dropextendedproperty ''tSQLt.FakeTable_OrgTableName'',''SCHEMA'',''' 
                           + PARSENAME(SchemaName,1) + N''',''TABLE'',''' + OriginalName + N''';'
                        ELSE ''
                   END
                 + NCHAR(13) + NCHAR(10) + N'    DELETE tSQLt.Private_RenamedObjectLog WHERE Id = ' + CAST(x.Id AS nvarchar) + N';'
                 + NCHAR(13) + NCHAR(10) + N'END'
         END
       + NCHAR(13) + NCHAR(10)
       + NCHAR(13) + NCHAR(10)
  FROM x;

--/*   <-Remove leading dashes to execute
PRINT @cmd;
--*/EXEC (@cmd);
于 2015-12-07T19:36:58.803 回答
5

我对 tSQLt 有同样的问题,并且能够使用表 tSQLt.Private_RenamedObjectLog 的内容来恢复所有内容

该表由 tSQLt 框架维护,并证明包含被伪造的原始表的名称,以及临时(即伪造)表的 SQL ObjectID。使用以下查询生成伪造表的列表,以及它们临时重命名的名称(由 tSQLt 生成的随机名称,例如tSQLt_tempobject_3815e077fea84c7c):

SELECT        
  ObjectId, OriginalName, 
  OBJECT_SCHEMA_NAME(ObjectId) AS SchemaName, 
  OBJECT_NAME(ObjectId) AS TemporaryName
FROM            
  tSQLt.Private_RenamedObjectLog

刷新 SSMS 中的对象资源管理器表明确实存在具有这些随机名称的表,并且它们确实包含我的原始数据(哇!!)。

然后我做了以下事情:

  1. 试了一个ROLLBACK TRANSACTION以防万一。它没有。
  2. 备份数据库(即使它搞砸了)
  3. 删除了假表(即具有原始名称的表,而不是临时名称)
  4. 将表(使用临时名称)重命名为原始名称,对每个表使用以下名称:

    EXEC sp_rename 'schema.tempname', 'originalname'

  5. 在我知道我的表回来后清除了表 tSQLt.Private_RenamedObjectLog ,使用

    DELETE FROM tSQLt.Private_RenamedObjectLog

制作一个自动生成恢复脚本的程序很容易!也许 tSQLt 中已经有一个 - 有人知道吗?

于 2014-07-15T13:12:43.557 回答
1

我知道我以前回答过这个问题!希望这个答案更有用。而且这个问题很老了。没有其他人使用SpyProcedure处理伪造函数或过程。

是的,这个问题很容易由在事务之外运行 tSQLt 测试的代码引起。tSQLt 运行事务中的每个测试,因此所有伪造等都会在测试结束时回滚。tSQLt 重命名表等以伪造它们,这通常会被回滚。运行SpyProcedure还会创建一个日志表,当您再次尝试运行 SpyProcedure 时可能会发生冲突。因此,如果您不在事务中运行测试,这些更改不会被回滚。幸运的是,tSQLt 包含恢复一切所需的所有信息。

首先尝试ROLLBACK TRANSACTION以防万一。如果没有,请检查您的原始表等是否记录在 tSQLt 的表中以获取伪造/重命名的内容:

SELECT ObjectId, OriginalName,
  OBJECT_SCHEMA_NAME(ObjectId) AS SchemaName, OBJECT_NAME(ObjectId) AS TemporaryName
FROM tSQLt.Private_RenamedObjectLog

如果返回任何记录,则很有可能可以恢复。但请先备份您的数据库!

现在我们生成 SQL 代码来删除伪造的对象(经过测试原始对象仍然存在)并将原始对象重命名为真实名称。据我所知,tSQLt 可以使用SpyProcedure伪造表、视图和函数以及过程,因此这应该适用于几乎所有情况。SpyProcedure 创建的日志表也会被删除:

DECLARE @SQL_work NVARCHAR(MAX) = '';
DECLARE @template NVARCHAR(MAX) = 
'IF OBJECT_ID(''%s.%s'') IS NOT NULL BEGIN -- check original still exists
  DROP %s %s.%s   -- deleted faked object
  EXEC sp_rename ''%s.%s'', ''%s''
END
';
SELECT
  @SQL_work = @SQL_work + 
    FORMATMESSAGE (CAST (@template AS NVARCHAR (MAX)),
      QUOTENAME(OBJECT_SCHEMA_NAME(r.ObjectId)), OBJECT_NAME(ObjectId),
      CASE WHEN type IN ('U', 'V') THEN 'TABLE'
           WHEN type IN ('FN','FS','TF','IF','FT') THEN 'FUNCTION'
           WHEN type = 'P' THEN 'PROCEDURE'
      END, 
      QUOTENAME(OBJECT_SCHEMA_NAME(r.ObjectId)), OriginalName,
      QUOTENAME(OBJECT_SCHEMA_NAME(r.ObjectId)), OBJECT_NAME(ObjectId),
      PARSENAME (OriginalName, 1)
    ) +
    CASE WHEN type = 'P' 
         THEN FORMATMESSAGE ('DROP TABLE %s.%s_SpyProcedureLog
',              QUOTENAME(OBJECT_SCHEMA_NAME(r.ObjectId)),
                PARSENAME (OriginalName, 1)
              )
         ELSE ''
    END +CHAR(13)+CHAR(10)
FROM
  tSQLt.Private_RenamedObjectLog AS r
JOIN 
  sys.objects AS o ON r.ObjectId = o.object_id
PRINT @SQL_work
PRINT 'DELETE FROM tSQLt.Private_RenamedObjectLog'

上面生成并打印代码来设置东西。我建议谨慎执行生成的脚本,即逐个执行部分,并且仅在其他一切正常DELETE时才执行最后一条语句。

我很想知道这是否对您有用,或者是否遇到任何问题以便改进代码。

于 2020-02-27T11:34:22.873 回答
0

请注意,每个 tSQLt 测试都只是一个存储过程。它们可以通过简单的 EXEC 执行。未能使用 tSQLt.Run 可能会在不创建事务的情况下执行测试过程。这意味着 FakeTable 等的效果不会在测试完成时回滚。由于 SQL Server 中的事务操作是 tSQLt 功能的核心原则,因此应注意一些。

在我们的组织中,我们提供了一个所有开发人员都应该使用的测试程序模板。模板做的第一件事是检查代码是否在事务中运行。如果不是,它会以适当的消息中止。这不会解决所有与事务相关的问题,但可以确保测试不会裸运行。

于 2020-11-02T16:45:47.710 回答