Не может получить доступ к базе данных из нескольких модульных тестов

У меня была строка подключения и куча моих модульных тестов, использующих ее, чтобы проверить логику некоторого класса, который применял к нему некоторые операции 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;
        }
    }
}

Я вызывал метод GetLocalDB в поле инициализации ctor amd моего тестового класса. После этого я получил следующую ошибку: «Процесс не может получить доступ к файлу blah log.ldf, потому что он используется другим процессом».

!!! Во всех тестах использовалась одна и та же строка подключения, я понятия не имею, что пошло не так. Это не может быть сбой модульных тестов, поскольку все, что я изменил, это строка подключения (была для уже существующей базы данных -> изменена на временную локальную (класс LocalDb))

Спасибо!

Обязательно: вы не должны проводить модульное тестирование базы данных, вы должны издеваться над ней.

Paul Swetz 22.01.2019 17:32

@PaulSwetz, это точка интеграционного теста (я перехожу с AzureStorage на SQL db, поэтому его нужно протестировать)

user9314289 22.01.2019 17:34

Создайте правильную базу данных вместо использования LocalDB и базы данных пользовательского экземпляра. SQL Server — это база данных сервер. Вы не будете использовать его как встроенную базу данных, особенно если вы мигрируете из облачного хранилища.

Panagiotis Kanavos 22.01.2019 17:36

@PanagiotisKanavos Я так и делал. Но мне сказали, что смысл в том, чтобы создать базу данных, проверить логику, а затем удалить ее. Я не имею никакого выбора.

user9314289 22.01.2019 17:37

LocalDB может обрабатывать только одно соединение за раз, и выполнение строки подключения в качестве прикрепленного файла действительно приводит к сбою. Если вы создаете локальную базу данных внутри Visual Studio и используете для этого стандартную строку подключения, вы, по крайней мере, не должны подвергаться ограничению одного пользователя, и ваш код должен работать.

Paul Swetz 22.01.2019 17:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
169
1

Ответы 1

Я также использовал localDb для тестирования в своем веб-приложении, и у меня была аналогичная проблема. Эта проблема может иногда возникать, если во время отладки вы остановились между ними или какой-то тест столкнулся с каким-либо исключением, и удаление локальной базы данных не произошло должным образом. Если это произойдет, то в следующий раз, когда вы начнете выполнять тест, он не создаст базу данных (несмотря на проверку ifdbexists) и столкнется с некоторым исключением, как вы упомянули. Чтобы проверить, я добавлял другое имя каждый раз, когда мы запускали набор тестов, чтобы база данных создавалась в начале выполнения теста и удалялась после выполнения всех тестов. блок finally, чтобы убедиться, что это происходит даже в случае исключения

Другие вопросы по теме