使用自动配置的localdb进行单元测试;我该如何清理?

时间:2012-09-05 02:04:20

标签: .net sql-server localdb

我已经配置了一个.mdf文件以及一个localdb连接字符串,用于单元测试,如下所示:

<connectionStrings>
        <add name="TestData" providerName="System.Data.SqlClient" connectionString="Data Source=(localdb)\v11.0; AttachDBFilename='|DataDirectory|\TestData.mdf'; Integrated Security=True"/>
</connectionStrings>

一旦我为我的测试正确配置了部署文件,这就可以很好地工作:.mdf的副本附加到LocalDB的默认实例,SqlClient连接到它而没有一丝配置。它只是有效。

但我如何在事后清理?在我的本地方框中,我可以定期使用SSMS手动分离旧的测试数据库,但在CI服务器上,显然最好让单元测试自行清理。

是否有类似的自动方式导致localdb数据库与实例分离?

5 个答案:

答案 0 :(得分:4)

这就是我删除localDB数据库的方法。我不喜欢的是.mdf也被删除了。我通过先将它复制到tem目录并使用副本创建数据库来克服这个问题。

var sc = new Microsoft.SqlServer.Management.Common.ServerConnection(your localDB SqlConnection here);
var server = new Microsoft.SqlServer.Management.Smo.Server(sc);
server.KillDatabase(dbName here);

希望这会有所帮助 菲尔

答案 1 :(得分:1)

是的,但您可能不需要它。

我目前正在使用一个进程,我将附加数据库附加到wbx911 / Tim Post的答案中。但是如果要附加原始数据库的副本(例如,由项目/解决方案构建创建的副本),则不需要对它们进行删除。如果没有连接到db,它将在下一次测试运行时被覆盖。

如果您附加了大量数据库副本,或者只是想清理附加的所有内容,那么您可以使用如下所示的存储过程,附加到localDb实例的主数据库(我个人使用的例子:

CREATE PROCEDURE [dbo].[DettachTestDatabases]
AS

BEGIN
DECLARE  @imax INT, 
         @i    INT 
DECLARE  @Name VARCHAR(255)

DECLARE  @DbsToDrop TABLE(RowID INT IDENTITY ( 1 , 1 ), 
                          name VARCHAR(255)
                         ) 

INSERT @DbsToDrop
SELECT [name]
  FROM [master].[sys].[databases]
  WHERE 
    --Remove all local dbs with *SomeText*.mdf or SomeOtherTextin name
    (name like '%SomeText%.mdf' or name like '%SomeOtherText%')
    -- Exclude VS test dbs (add more as required...)
    and (name not like '%TESTS.%');

SET @imax = @@ROWCOUNT 
SET @i = 1 

WHILE (@i <= @imax) 
  BEGIN 
        SELECT @Name = name 
        FROM   @DbsToDrop 
        WHERE  RowID = @i

        EXEC master.dbo.sp_detach_db @dbname = @Name;

        PRINT 'Dettatched ' + @Name;

        SET @i = @i + 1;
  END   --while

END --sp
GO

我正在使用命名约定来确定要删除的dbs(因此是类似的语句)。 Visual Studio附加了几个db(或者至少我认为它们属于VS)来管理测试结果,我不想分离,(因此“不喜欢”条件)。 ther是一个master.dbo.sp_detach_db,它按名称分离,所以你真的只需要引用名称,但由于localdb名称可能有点疯狂,查找将很有用。

我在测试结束时还没有自动化,因为我觉得不需要(因为覆盖工作正常),但我可以随时随地在SSMS中执行它。但是如果您觉得有必要,您只需要从清理代码中管理对SP的调用。

附加dbs在某种程度上是一个昂贵的过程。我选择了一个清理方法/ SP来删除任何变量数据并重新安装表以使db处于一致状态,这非常快,而不是重新附加数据库。

如果你发现任何改进点,我真的很想知道它们!

答案 2 :(得分:1)

您使用的单元测试框架是否允许您附加清理方法(即AssemblyAttribute在MS测试中,OneTimeTearDown在NUnit 3中,shared context在XUnit中?如果是这样,您可以连接类似下面的内容,以便在测试结束时自动分离数据库。

public static void CleanUp()
{
    using (var connection = new SqlConnection(ConnectionString))
    {
        connection.Open();

        using (var command = connection.CreateCommand())
        {
            var sql = "DECLARE @dbName NVARCHAR(260) = QUOTENAME(DB_NAME());\n" +
                "EXEC('exec sp_detach_db ' + @dbName + ';');";
            command.CommandText = sql;
            command.ExecuteNonQuery();
         }
    }
}

答案 3 :(得分:0)

如果您正在讨论单元测试,例如xUnit,那么您可以添加到测试本身的属性,以便对任何触及数据库的内容进行回滚。

实际上,它在企业级事务处理中运行测试。这使得每个测试的数据都保持稳定,您不需要模拟数据库;它可以成为一种有效的验收测试。

例如(在xUnit中):

[Fact]
[AutoRollback]
public void Should_Insert_Valid_Data()
{
    // Arrange: presume there's an object, sut, that supports insert,
    // and you have some data

    // Act:
    int key = sut.Insert(data);

    // Assert:
    Assert.True(key > 0, "Expected a primary key to be returned");
    var inserted = sut.Get(key);
    Assert.Equal<DataClassName>(data, inserted, CompareAllButKey);
}

(fyi:“sut”是“被测试的情况”或“被测试的对象”)

在“Strategies for Isolating the Database in Tests”上看到这篇文章可能会有所帮助。该文章的最后还有一些其他不错的链接。

BTW,我使用xUnit作为示例,但其他测试框架具有类似的属性来控制回滚。

答案 4 :(得分:-3)

像这样:

 // ProjectPath/bin/Debug/dbfile.mdf

 [TestInitialize]
 public void SetUp()
 {           
    AppDomain.CurrentDomain.SetData(
      "DataDirectory", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ""));
  }