无法通过多个单元测试访问数据库

时间:2019-01-22 16:30:33

标签: c# sql .net sql-server database

我有一个连接字符串和一堆单元测试使用它来测试正在对其应用某些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 方法。 之后,出现以下错误“该进程无法访问文件 log.ldf ,因为它已被另一个进程使用”

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

谢谢!

1 个答案:

答案 0 :(得分:0)

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