【发布时间】:2019-01-22 16:30:33
【问题描述】:
我有连接字符串和一堆我的单元测试使用它来测试一些应用了一些 CRUD 操作的类的逻辑。所以我将它作为测试类中的私有常量字段传递并共享给我的测试。一切正常!
但后来我意识到我必须将其作为集成测试来进行。所以我决定使用静态助手类通过会话创建数据库,以便我测试使用它然后删除。
类如下:
public static class LocalDB
{
public const string DB_DIRECTORY = "Data";
public static string GetLocalDB(string dbName, bool deleteIfExists = false)
{
try
{
var outputFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), DB_DIRECTORY);
var mdfFilename = dbName + ".mdf";
var dbFileName = Path.Combine(outputFolder, mdfFilename);
var logFileName = Path.Combine(outputFolder, $"{dbName}_log.ldf");
if (!Directory.Exists(outputFolder))
{
Directory.CreateDirectory(outputFolder);
}
if (File.Exists(dbFileName) && deleteIfExists)
{
if (File.Exists(logFileName)) File.Delete(logFileName);
File.Delete(dbFileName);
CreateDatabase(dbName, dbFileName);
}
else if (!File.Exists(dbFileName))
{
CreateDatabase(dbName, dbFileName);
}
var connectionString = string.Format(@"Data Source=(LocalDB)\v11.0;AttachDBFileName={1};Initial Catalog={0};Integrated Security=True;", dbName, dbFileName);
CreateTable(connectionString, "Cpa", dbName);
return connectionString;
}
catch(Exception ex)
{
throw;
}
}
public static bool CreateDatabase(string dbName, string dbFileName)
{
try
{
var connectionString = @"Data Source=(LocalDB)\v11.0;Initial Catalog=master;Integrated Security=True";
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var cmd = connection.CreateCommand();
DetachDatabase(dbName);
cmd.CommandText = string.Format("CREATE DATABASE {0} ON (NAME = N'{0}', FILENAME = '{1}')", dbName, dbFileName);
cmd.ExecuteNonQuery();
cmd.Dispose();
}
return File.Exists(dbFileName);
}
catch
{
throw;
}
}
public static bool DetachDatabase(string dbName)
{
try
{
var connectionString = $@"Data Source=(LocalDB)\v11.0;Initial Catalog=master;Integrated Security=True";
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var cmd = connection.CreateCommand();
cmd.CommandText = $"exec sp_detach_db '{dbName}'";
cmd.ExecuteNonQuery();
cmd.Dispose();
return true;
}
}
catch(Exception ex)
{
return false;
}
}
public static bool CreateTable(string connectionString, string tableName, string dbName)
{
connectionString = connectionString.Replace("master", dbName);
try
{
using (var connection = new SqlConnection(connectionString))
{
var createTableQuery = $@"CREATE TABLE {tableName}(
CrmId nvarchar(50) NOT NULL,
Service nvarchar(25) NOT NULL,
RecurringReference nvarchar(50),
ExpiryDate datetime,
CardNumber nvarchar(50),
Enabled bit,
Brand nvarchar(50),
CpaType nvarchar(50),
Channel nvarchar(50)
);";
var command = new SqlCommand(createTableQuery, connection);
connection.Open();
var reader = command.ExecuteReader();
reader.Dispose();
return true;
}
}
catch (Exception ex)
{
return false;
}
}
}
我在我的测试类的 ctor amd 初始化字段中将其称为 GetLocalDB 方法。 之后我收到以下错误“进程无法访问文件 blah log.ldf 因为它被另一个进程使用”
!!! 所有测试都使用相同的连接字符串,我不知道出了什么问题。对于我所做的所有更改,不可能是单元测试失败是连接字符串(用于已经存在的 db -> 更改为临时本地(LocalDb 类))
谢谢!
【问题讨论】:
-
强制性:你不应该对你应该模拟的数据库进行单元测试。
-
@PaulSwetz 这是集成测试的重点(我正在从 AzureStorage 迁移到 SQL db,因此必须对其进行测试)
-
创建一个合适的数据库,而不是使用 LocalDB 和用户实例数据库。 SQL Server 是一个数据库服务器。您不会将其用作嵌入式数据库,尤其是当您从云存储迁移时
-
@PanagiotisKanavos 我就是这样做的。但有人告诉我重点是创建数据库,测试逻辑,然后删除它。我别无选择。
-
LocalDB 一次只能处理一个连接,并且将连接字符串作为文件附件执行确实使这一点成为崩溃的现实。如果您在 Visual Studio 中创建本地数据库并使用标准连接字符串,那么您至少不应该受到单用户限制,并且您的代码应该可以工作。
标签: c# sql .net sql-server database