Sql – NUnit [TearDown] fails — what process is accessing the files

ioexceptionnunitsqliteunit testing

Final Edit: I found a solution to the problem (at the bottom of the question).

I've got an Nunit problem that's causing me grief. Edit: actually it looks more like a SQLite problem, but I'm not 100% certain yet.

My TestFixture has a setup that generates a random filename that's used as a SQLite database in each of my tests.

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

Each of my tests use this construct in each method that accesses the database:

[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;
}

So that one helper method Connect() is used by all the methods. I'm assuming that the using() { } construct is calling Dispose() on the connection at the end of TestMethod() and freeing up the connection to the SQLite database file.

The problem I have is in my [TearDown] method:

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

With every test I get an exception:

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

All of the tests fail when they get to the [TearDown], so I end up with a directory full of temporary databse files (one per test, each with a different name) and a whole bunch of failed tests.

What process is accessing the file? I don't get how a second process could be accessing the file. The connection has completely gone out of scope and been Dispose()d by the time I'm trying to delete the file, so it can't be something SQLite related. Can it?

Note that I get the same result if I run all the tests or just a single test.

Update: So I tried Dispose()ing of my DbCommand objects as well, since I wasn't doing that (I assumed that every other ADO.NET provider that Dispose()ing the DbConnection also Dispose()s any commands on that connection.) So now they look like:

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

        }
    }
}

It didn't make any difference — the File.Delete() line still throws an IOException. 🙁

If I remove that one line in [TearDown] then all my tests pass, but I'm left with a whole bunch of temporary database files.

Another Update:
This works just fine:

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);            

I don't understand!

Update: Solution found:

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

I ran the unit tests through the debugger, and when the [TearDown] method starts there are definitely no references to the SQLite DbConnection around any more. Forcing a GC must clean them up though. There must be a bug in SQLite.

Best Answer

Thanks for the posted answer at the bottom. I was digging for hours for exactly the same case and

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

did the trick.

Related Topic