在处理数据库数据时,xUnit测试出现死锁

5

我有两个类,每个类都包含与数据库集成测试相关的内容。在每个类的构造函数中,我放置了一个重置数据库的方法:

public class FirstClassSpec {
    public FirstClassSpec() {
        var dataSetup = new DataSetup();
        dataSetup.CleanTables();
    }

    [Fact]
    public async Task FirstTest() {
        using(var conn = new SqlConnection("connStringHere")){
            var result = await conn.ExecuteAsync("someSqlCommand");
            Assert.True(result > 0);
        }
    }
}

public class SecondClassSpec {
    public SecondClassSpec() {
        var dataSetup = new DataSetup();
        dataSetup.CleanTables();
    }

    [Fact]
    public async Task SecondTest() {
        using(var conn = new SqlConnection("connStringHere")){
            var result = await conn.ExecuteAsync("someSqlCommand");
            Assert.True(result > 0);
        }
    }
}

public class DataSetup {
    public void CleanTables() {
        using(var conn = new SqlConnection("connStringHere")){
            await conn.Execute("someSqlCommandToCleanTables");
        }
    }
}

为了在Visual Studio 2015上运行测试,我使用Test Explorer中的Run All Tests。出现了以下信息:

Transaction (Process ID {someID}) was deadlocked on lock resources with another process

只有当我运行所有的测试时才会出现这个问题。如果我一个一个地运行每个测试,或者从同一个类中运行多个测试,就不会发生死锁。
我发现是在类的构造函数中调用的CleanTables()方法导致了这个问题。我猜测测试是并行运行的,并且两个类同时调用了CleanTables()。因此,我尝试将CleanTables()改为异步方法。
public async Task<int> CleanTables() {
    using(var conn = new SqlConnection("connStringHere")){
        return await conn.Execute("someSqlCommandToCleanTables");
    }
}

然后在类的构造函数中,我这样调用它:

public FirstClassSpec() {
    var dataSetup = new DataSetup();
    dataSetup.CleanTables().Wait();
}

public SecondClassSpec() {
    var dataSetup = new DataSetup();
    dataSetup.CleanTables().Wait();
}

但是现在当我尝试运行所有测试时,测试正在运行,但它们永远不会完成,我也无法得到结果。

我的问题是,死锁发生的原因是什么?为什么将 CleanTables() 方法改为异步会导致运行测试永远不会完成?我真的需要在每次运行测试之前清理表格。

----------------- 更新 -----------------------

我已经尝试为所有测试类添加了 [Collection["CollectionName"]],每个类都有不同的名称:

[Collection["FirstSpec"]]
public class FirstClassSpec {
    //....
}

[Collection["SecondSpec"]]
public class FirstClassSpec {
    //....
}

但是死锁仍然会发生...

----------------- 更新2 -----------------------

事实证明,具有相同集合名称的类将被顺序执行,这解决了死锁问题。


你尝试过使用Sql Server Profiler分析死锁吗?它在我以前的工作中帮了我很多。 - Ryan D
1个回答

8
您的猜测关于测试并行运行是正确的。默认情况下,xUnit不会同时运行同一类中的测试。因此,要解决您的问题,您可以将所有测试移动到一个类中。或者,您可以使用[Collection("My Collection")]修饰您的类,以指示两个类中的测试不应并行运行。
您可以在此处了解有关xUnit如何决定并行运行测试的更多信息:https://xunit.github.io/docs/running-tests-in-parallel.html

谢谢你的解决方案!但是我不想把所有的测试都移到一个类中(这样会很难维护)。我今天稍后会尝试用 [Collection["My Collection"]] 装饰我的类。如果可以的话,我会将你的答案标记为正确答案! :) - samAlvin
我已经尝试使用[Collection ["CollectionName"]]装饰所有测试类,每个类都有不同的名称。但死锁仍然发生。我已更新我的问题。 - samAlvin
每个类应该有相同的集合名称。 - Mike Hixson
它运行了!现在我懂了,因为类的collectionName相同,它们被顺序执行了...感谢你的时间,伙计! - samAlvin
哇,我在困扰ConfigureAwait几天后并不认为这会奏效,但是出于绝望我还是试了一下... 突然奏效了!给这个人一杯酒庆祝吧! :-) - Riegardt Steyn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接