15

我已经配置了一个.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 数据库从实例中分离出来?

4

5 回答 5

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);

希望这有助于菲尔

于 2012-09-12T19:32:32.070 回答
1

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

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

如果您有很多附加的数据库副本,或者只是不想清理附加的所有内容,那么您可以使用如下所示的存储过程,附加到 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

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

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

附加数据库有点昂贵的过程。我选择了一种清理方法/SP,它删除任何可变数据并重新填充表以使数据库处于一致状态,这非常快,而不是重新附加数据库。

如果您发现任何改进点,我真的很想了解它们!

于 2013-11-14T15:23:56.173 回答
1

您正在使用的单元测试框架是否允许您附加清理方法(即MS Test 中的 AssemblyAttribute、NUnit 3 中的 OneTimeTearDown、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();
         }
    }
}
于 2016-10-26T17:40:00.683 回答
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);
}

(仅供参考:“sut”是“正在测试的情况”或“正在测试的主题”)

阅读这篇关于“在测试中隔离数据库的策略”的文章可能会有所帮助。那篇文章的末尾还有一些其他不错的链接。

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

于 2013-09-21T07:36:48.353 回答
-3

像这样 :

 // ProjectPath/bin/Debug/dbfile.mdf

 [TestInitialize]
 public void SetUp()
 {           
    AppDomain.CurrentDomain.SetData(
      "DataDirectory", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ""));
  }
于 2013-02-04T05:36:51.040 回答