【问题标题】:xUnit test deadlockxUnit 测试死锁
【发布时间】:2016-05-15 04:13:12
【问题描述】:

当我运行我的 xUnit 单元测试时,我有时会在一个或多个测试中收到一条错误消息,例如“事务(进程 ID 58)在锁定资源上与另一个进程死锁,并已被选为死锁受害者”,似乎随机。如果我自己重新运行任何失败的测试,它就会通过。

我应该怎么做才能防止这种情况发生?是否可以选择一个接一个地运行测试而不是一次运行所有测试?

(注意,我正在 Visual Studio 2015 下的 ASP.Net 5 MVC 控制器中通过 API 方法运行测试)

这是我偶尔失败的测试之一的示例:

[Fact]
private void TestREAD()
{
    Linq2SQLTestHelpers.SQLCommands.AddCollections(TestCollections.Select(collection => Convert.Collection2DB(collection)).ToList(), TestSettings.LocalConnectionString);
    foreach (var testCollection in TestCollections)
    {
        var testCollectionFromDB = CollectionsController.Get(testCollection.Id);
        Assert.Equal(testCollection.Description, testCollectionFromDB.Description);
        Assert.Equal(testCollection.Id, testCollectionFromDB.Id);
        Assert.Equal(testCollection.IsPublic, testCollectionFromDB.IsPublic);
        Assert.Equal(testCollection.LayoutSettings, testCollectionFromDB.LayoutSettings);
        Assert.Equal(testCollection.Name, testCollectionFromDB.Name);
        Assert.Equal(testCollection.UserId, testCollectionFromDB.UserId);
    }
}

测试调用有两个方法,这里是控制器方法:

[HttpGet("{id}")]
public Collection Get(Guid id)
{
    var sql = @"SELECT * FROM Collections WHERE id = @id";
    using (var connection = new SqlConnection(ConnectionString))
    {
        var collection = connection.Query<Collection>(sql, new { id = id }).First();
        return collection;
    }
}

这是辅助方法:

public static void AddCollections(List<Collection> collections, string connectionString)
{
    using (var db = new DataClassesDataContext(connectionString))
    {
        db.Collections.InsertAllOnSubmit(collections);
        db.SubmitChanges();
    }
}

(请注意,我在控制器方法中使用 Dapper 作为微 ORM,因此,为了避免在测试中可能出现重复错误,我在测试中使用 LINQ to SQL 来设置和清理 -测试数据。)

单元测试类的构造函数和Dispose方法中也有数据库调用。如果需要,我可以将它们添加到帖子中。

【问题讨论】:

  • 您可能想分享您正在编写的测试类型 - 对您的 async / Task 管理等的一些见解。现在您可以做任何事情,显然有些人正在取得成功,我们只需要弄清楚您的上下文中泄漏/锁定的内容......
  • 完成了,够了吗?
  • 嗯 - 看起来并不能归咎于那里的 xUnit。您是否正在破坏连接 [及其相关的锁]?
  • 我会添加控制器方法,我不这么认为。

标签: linq-to-sql dapper xunit.net


【解决方案1】:

好的,所以看起来您的应用程序中出现了一个普通的死锁案例,并且需要处理它 - 您在应用程序方面的计划是什么?

测试及其数据操纵可能会成为同一件事的牺牲品。 xUnit 没有任何东西可以解决这个问题,我强烈认为它不应该。

所以在测试和应用中,你都需要失败/重试管理。

对于一个网络应用程序,你需要给他们一张鲸鱼的照片,然后让他们再试一次,但最终你想要一个真正的解决方案。

对于测试,你不想要鲸鱼并且肯定想要处理它,即不要脆弱。

我将使用 Poly 将重试修饰包裹在应用程序或测试中容易出现重大故障的任何内容上——您的练习是找出您的上下文中的重大失败是什么.


在正常情况下,具有单个读取器/写入器同步操作的数据库不应出现死锁。分析它发生的原因是在数据库端进行分析的问题。那边的工具也可能会很快向您展示,例如您的整个被测系统的某些方面导致了竞争性工作。

(显然你的 sn-ps 是不完整的,因为 CollectionsController.Get(testCollection.Id) 和控制器方法不是 static 的事实之间存在脱节 - 虽然这个讨论的重点不应该放在 IMO 的那个级别上)

【讨论】:

  • 控制器的构造函数中几乎没有初始化,我只是传入一个包装为 IOptions 的连接字符串用于依赖注入。测试似乎随机失败,因此它们都“容易出现重大失败”。我不确定我可以使用哪些工具来分析数据库端的死锁,但我会进行调查。谢谢。
  • @dumbledad SQL Profiler 可以显示它们。重新“测试似乎随机失败”,要弄清楚的一件事是它是测试夹具设置还是导致问题的控制器中的东西 - 即如果它是实际的控制器,那么你可以将它视为一个错误需要通过在那里进行一些受限重试来修复的控制器
  • Profiler 显示了一个死锁,该死锁似乎来自我的 INSTEAD OF DELETE 触发器,该触发器级联删除。所以我认为你是对的,这是一个可能在实时代码中咬住我的僵局,而不仅仅是一个测试。我最终遵循了here 的建议并删除了触发器并将其重写为存储过程,现在我的测试全部运行没有错误。
  • 有趣。我对用 SP 替换触发器有点不耐烦——无论你以何种方式构建和排序使系统在第一个实例中容易死锁的依赖项,它们仍然逍遥法外......
  • 也许排序是关键。在我的 DELETE_USER 触发器中,我删除了必要的项目和集合,并依靠 DELETE_ITEM 和 DELETE_COLLECTION 触发器来清理。在我的 Delete_User 存储过程中,我遍历所有表,然后离开分支,然后进行删除。我不调用 Delete_Item 也不调用 Delete_Collection 存储过程。
猜你喜欢
  • 2017-12-14
  • 2019-08-20
  • 2010-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多