最终编辑:我找到了问题的解决方案(在问题的底部)。
我有一个让我很伤心的 Nunit 问题。编辑:实际上它看起来更像是一个 SQLite 问题,但我还不是 100% 确定。
我的 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 对象的 Dispose()ing,因为我没有这样做(我假设 Dispose()ing DbConnection 的所有其他 ADO.NET 提供程序也 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 中一定有一个错误。