NUnit [TearDown]失败 - 访问我的文件的进程是什么?

时间:2008-12-17 13:46:55

标签: unit-testing sqlite nunit ioexception

最终编辑:我找到了问题的解决方案(问题的底部)。

我遇到了一个让我感到悲伤的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相关。可以吗?

请注意,如果我运行所有测试或只进行一次测试,我会得到相同的结果。

更新:所以我也尝试了Dispose()我的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中必定存在错误。

11 个答案:

答案 0 :(得分:8)

感谢您在底部发布的答案。对于完全相同的情况我正在挖掘几个小时

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

做了这个伎俩。

答案 1 :(得分:1)

尝试在dbconnection上调用Close

确保sqllite进程已终止

您可以使用Unlocker(免费)实用程序

查看您的文件被锁定的进程

这可能是'known' issue with SqlLite;论坛的玩笑建议关闭连接并处理命令,并建议将在未来版本中修复此问题(因为此行为与其他ADO提供商不一致)

答案 2 :(得分:0)

每次测试后都会执行拆除。

  

此属性在TestFixture中使用,以提供在每个测试方法运行后执行的一组通用函数。

您应该尝试使用 TestFixtureTearDown 删除所有内容:

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

也许有一个测试正在使用您尝试在其他测试中删除的文件。 < - [Stewart]正如我在问题中所述,当我只运行一次测试时会发生这种情况,所以这是不可能的。

更新 您没有提供有关您正在做的事情的足够信息。您是否尝试在测试文件中仅使用1个测试来清理所有测试文件并尝试一下? [Stewart]是的。如果它工作 [Stewart](它没有)那么就是你有多个测试问题(他们互相访问)。您需要减少问题才能找到来源。然后,回到这里我们会帮助你。目前我们只能猜测我们可以给你。 [斯图尔特]我已经完成了你建议的问题的这些减少,你会发现它们是我原来的问题!

答案 3 :(得分:0)

我需要查看您的文件名创建逻辑,但您可能正在打开文件来创建它,但是一旦创建它就不会关闭它。我想如果您使用System.IO.Path.GetTempFileName(),它将只创建文件并返回文件名,文件已关闭。如果您正在使用File.Open进行自己的随机名称生成,那么您需要确保它随后关闭。

另一方面,我衷心地建议采用模拟策略来抽象数据库,而不是在单元测试中从实际数据库中读取/写入。单元测试的重点是它们应该非常快,文件I / O会使这些速度变慢。

答案 4 :(得分:0)

您是否正在运行防病毒软件?某些AV产品在看到文件关闭时会对其进行扫描。如果AV软件在您删除它时仍然打开文件,您将看到此问题。您可以在稍微延迟后重试删除。

我也看到搜索索引器会发生这种情况。

答案 5 :(得分:0)

第一步是查找谁持有相关文件的句柄。

获取Process Explorer的副本运行它。按Ctrl + F并输入文件名。它会显示一个访问它的进程列表。

答案 6 :(得分:0)

文件是否可能正在关闭(即,SQLLite不会立即释放它)?

你可以尝试将db关闭在延迟循环中(每次尝试之间可能是一秒),并且只在循环几次(大约5次)迭代后抛出异常。

答案 7 :(得分:0)

您用什么打开数据库?您是否使用here中的ADO 2.0连接器。如果有一个应用程序使用它,我可以与它进行多个连接(关闭/打开)。如果您不使用此连接器,您可以尝试一下。你的方法Connect()返回什么?

答案 8 :(得分:0)

感谢您指出这一点!

问题与SQLite无关,而是与内存管理有关。运行测试后,指向文件的对象不再具有范围,但它们仍然存在于内存中。

因此,仍然存在对文件的引用,您无法删除/移动它们。

答案 9 :(得分:0)

我知道这个答案已经晚了一年多,但对于将来读书的人来说......

我遇到了类似的问题 - 由于SQLite数据库文件保持打开状态,尝试在测试之间删除测试数据库失败。我在我的代码中将问题跟踪到未明确关闭的SQLiteDataReader对象。

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

答案 10 :(得分:0)

调用静态方法

SqliteConnection.ClearAllPools()

在此调用之后,数据库文件被解锁,您可以删除[TearDown]中的文件。