NUnit [TearDown] не работает - какой процесс имеет доступ к моим файлам?

Final Edit: Я нашел решение проблемы (в нижней части вопроса).

У меня проблема Nunit, которая вызывает у меня горе. Изменить: На самом деле это больше похоже на проблему SQLite, но я еще не уверен на 100%.

В My TestFixture есть настройка, которая генерирует случайное имя файла, которое используется в качестве базы данных SQLite в каждом из моих тестов.

[Setup]
public void Setup()
{
    // "filename" is a private field in my TestFixture class
    filename = ...; // generate random filename
}

Каждый из моих тестов использует эту конструкцию в каждом методе, который обращается к базе данных:

[Test]
public void TestMethod()
{
    using (var connection = Connect())
    {
        // do database activity using connection

        // I've tried including this line but it doesn't help
        // and is strictly unnecessary:
        connection.Close();
    }
}

private DbConnection Connect()
{
    var connection = DbProviderFactories.GetFactory("System.Data.SQLite").CreateConnection();
    connection.ConnectionString = "Data Source=" + filename;
    connection.Open();
    return connection;
}

Итак, для всех методов используется один вспомогательный метод Connect(). Я предполагаю, что конструкция using() { } вызывает Dispose() в соединении в конце TestMethod() и освобождает соединение с файлом базы данных SQLite.

Проблема у меня есть в моем методе [TearDown]:

    [TearDown]
    public void Cleanup()
    {
        File.Delete(filename); // throws an IOException!
    }

С каждым тестом я получаю исключение:

System.IO.IOException: The process cannot access the file 'testdatabase2008-12-17_1030-04.614065.sqlite' because it is being used by another process.

Все тесты терпят неудачу, когда они добираются до [TearDown], поэтому я заканчиваю каталогом, полным временных файлов базы данных (по одному на тест, каждый с другим именем) и целым рядом неудачных тестов.

Какой процесс имеет доступ к файлу? Я не понимаю, как второй процесс может получить доступ к файлу. connection полностью вышел из области действия и был Dispose() d к моменту, когда я пытаюсь удалить файл, поэтому он не может быть связан с SQLite. Может ли это?

Обратите внимание, что я получаю тот же результат, если я запускаю все тесты или только один тест.

Обновление: Итак, я попытался удалить или удалить мои объекты DbCommand, так как я этого не делал (я предположил, что каждый другой поставщик ADO.NET, который Dispose() использует DbConnection также Dispose() s любые команды в этом соединении.) Теперь они выглядят так:

[Test]
public void TestMethod()
{
    using (var connection = Connect())
    {
        using (var command = connection.CreateCommand())
        {
        // do database activity using connection

        }
    }
}

Это не имело никакого значения - строка File.Delete() по-прежнему вызывает исключение IOException.: - (

Если я удалю эту строку в [TearDown], все мои тесты пройдут, но у меня останется целая куча временных файлов базы данных.

Другое обновление: Это прекрасно работает:

var filename = "testfile.sqlite";
using (var connection = BbProviderFactories.GetFactory("System.Data.SQLite").CreateConnection())
{
    connection.ConnectionString = "Data Source=" + filename;
    connection.Open();
    var createCommand = connection.CreateCommand();
    createCommand.CommandText =
        "CREATE TABLE foo (id integer not null primary key autoincrement, bar text not null);";
    createCommand.ExecuteNonQuery();
    var insertCommand = connection.CreateCommand();
    insertCommand.CommandText = "INSERT INTO foo (bar) VALUES (@bar)";
    insertCommand.Parameters.Add(insertCommand.CreateParameter());
    insertCommand.Parameters[0].ParameterName = "@bar";
    insertCommand.Parameters[0].Value = "quux";
    insertCommand.ExecuteNonQuery();
}
File.Delete(filename);            

Я не понимаю!

Обновление: найденное решение:

    [TearDown]
    public void Cleanup()
    {
        GC.Collect();
        File.Delete(filename);
    }

Я запускал модульные тесты через отладчик, и когда запускается метод [TearDown], определенно нет ссылок на SQLite DbConnection. Принуждение GC должно их очистить. В SQLite должна быть ошибка.

Ответы

Ответ 1

Спасибо за опубликованный ответ внизу. Я копал часами для одного и того же случая и

GC.Collect ();
GC.WaitForPendingFinalizers ();

сделал трюк.

Ответ 2

попробуйте вызвать Close on dbconnection

убедитесь, что процесс sqllite завершен

вы можете видеть, какой процесс заблокирован вашим файлом с помощью утилиты Unlocker (бесплатно)

это может быть "известная" проблема с SqlLite; форум подшучивает над закрытием соединения и распоряжением командой и предполагает, что это будет исправлено в будущей версии (поскольку это поведение не согласуется с другими поставщиками ADO)

Ответ 3

Сброс выполняется после каждого теста.

Этот атрибут используется внутри TestFixture для обеспечения общего набора функций, которые выполняются после запуска каждого тестового метода.

Вам следует попытаться удалить их все с помощью TestFixtureTearDown:

    [TestFixtureTearDown]
    public void finish()
    {
        //Delete all file
    }

Возможно, один тест использует файл, который вы пытаетесь удалить в другом тесте. < - [Стюарт] Как я сказал в вопросе, это происходит, когда я запускаю только один тест, поэтому это невозможно.

Update Вы не даете достаточно информации о том, что делаете. Вы пытаетесь очистить весь тестовый файл только одним тестом в тестовом файле и попробовать? [Стюарт] Да. Если он работает [Стюарт] (это не так), то это значит, что у вас есть несколько тестовых проблем (они обращаются друг к другу). Чтобы найти источник, вам нужно сократить проблему. Затем вернитесь, мы поможем вам. на данный момент он только догадывается, что мы можем дать вам. [Стюарт] Я уже сделал эти сокращения проблемы, которые вы предлагаете, вы обнаружите, что они в моем первоначальном вопросе!

Ответ 4

Мне нужно будет увидеть логику создания вашего файла, но возможно, что вы открываете файл для его создания, но не закрываете его после его создания. Я думаю, что если вы используете System.IO.Path.GetTempFileName(), он просто создаст файл и вернет вам имя файла с закрытым файлом. Если вы делаете свое собственное генерирование случайных имен и используете File.Open, вам нужно будет убедиться, что он закрыт впоследствии.

С другой стороны, я сердечно рекомендую придерживаться насмешливой стратегии абстрагирования базы данных, а не чтения/записи из реальной базы данных в своих модульных тестах. Точка модульных тестов заключается в том, что они должны быть очень быстрыми, и файлы ввода/вывода будут действительно замедлять их.

Ответ 5

У вас работает Антивирус? Некоторые AV-продукты просматривают файл, когда видят, что они закрыты. Если у программного обеспечения AV по-прежнему открыт файл, когда вы его удаляете, вы увидите эту проблему. Вы можете повторить удаление после небольшой задержки.

Я также видел, как это происходит с поисковыми индексаторами.

Ответ 6

Первый шаг - найти, кто держит дескриптор соответствующего файла.

Получить копию Process Explorer Запустить. Нажмите Ctrl + F и введите имя файла. Он покажет вам список процессов, обращающихся к нему.

Ответ 7

Возможно ли, что файл находится в процессе закрытия (I.e. SQLLite не выпускает его немедленно)?

Вы можете попробовать положить db close в цикл задержки (возможно, одну секунду между каждой попыткой) и только бросать исключение после нескольких (5 или около того) итераций через цикл.

Ответ 8

Что вы используете для открытия своей базы данных? Вы используете разъем ADO 2.0 от здесь. Если у вас есть приложение, которое его использует, и я могу сделать с ним несколько соединений (закрыть/открыть). Если вы не используете этот разъем, вы можете попробовать. Что возвращает ваш метод Connect()?

Ответ 9

Спасибо, что указали это!

Проблема не связана с SQLite, а скорее с управлением памятью в целом. После запуска теста объекты, указывающие на файлы, больше не имеют области видимости, но они все еще существуют в памяти.

Следовательно, все еще есть ссылки на файлы, и вы не можете их удалить или переместить.

Ответ 10

Я знаю, что этот ответ уже более года, но для тех, кто читает в будущем...

У меня была аналогичная проблема для вас - попытка удалить тестовые базы данных между испытаниями не удалась из-за того, что файл базы данных SQLite остался открытым. Я проследил проблему в моем коде, чтобы объект SQLiteDataReader не был явно закрыт.

SQLiteDataReader dr = cmd_.ExecuteReader();
while (dr.Read())
{
    // use the DataReader results
}
dr.Close();  //  <-- necessary for database resources to be released

Ответ 11

Вызов статического метода

SqliteConnection.ClearAllPools()

После этого вызова файл базы данных разблокируется, и вы можете удалить файл в [TearDown].