首页 > 解决方案 > 无法从多个单元测试访问数据库

问题描述

我有连接字符串和一堆我的单元测试使用它来测试一些应用了一些 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因为它被另一个进程使用”

!!!所有测试都使用相同的连接字符串,我不知道出了什么问题。对于我所做的所有更改,不可能是单元测试失败是连接字符串(用于已经存在的数据库-> 更改为临时本地(LocalDb 类))

谢谢!

标签: c#sql.netsql-serverdatabase

解决方案


我还在我的 Web 应用程序中使用 localDb 进行测试,并且遇到了类似的问题。如果在调试期间您在中间停止或某些测试遇到任何异常并且没有正确处理 localdb,则有时可能会发生此问题。如果发生这种情况,那么下次当您开始运行测试时,它不会创建数据库(尽管检查了 ifdbexists)并遇到您提到的一些异常。为了检查我每次运行一组测试时添加了一个不同的名称,以便在运行测试开始时创建 db,并在执行所有测试后删除。由于每次都给出不同的名称并不理想,请尝试将 localdb 放置在finally 阻止以确保即使在异常情况下也会发生


推荐阅读