Сбой NUnit [TearDown] - какой процесс обращается к моим файлам? - PullRequest
18 голосов
/ 17 декабря 2008

Окончательное редактирование: Я нашел решение проблемы (внизу вопроса).

У меня проблема с монахиней, которая вызывает у меня горе. Редактировать: на самом деле это больше похоже на проблему 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. Может ли это?

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

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

[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. Принудительный сборщик мусора должен их очистить. В SQLite должна быть ошибка.

Ответы [ 11 ]

8 голосов
/ 14 сентября 2012

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

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

сделал свое дело.

1 голос
/ 17 декабря 2008

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

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

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

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

0 голосов
/ 21 октября 2010

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

SqliteConnection.ClearAllPools ()

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

0 голосов
/ 25 января 2010

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

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

SQLiteDataReader dr = cmd_.ExecuteReader();
while (dr.Read())
{
    // use the DataReader results
}
dr.Close();  //  <-- necessary for database resources to be released
0 голосов
/ 02 июля 2009

Спасибо за указание на это!

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

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

0 голосов
/ 17 декабря 2008

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

0 голосов
/ 17 декабря 2008

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

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

0 голосов
/ 17 декабря 2008

Первым шагом было бы найти, кто держит дескриптор файла, о котором идет речь.

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

0 голосов
/ 17 декабря 2008

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

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

0 голосов
/ 17 декабря 2008

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

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

...