嵌套 TransactionScope 在测试中失败

Posted

技术标签:

【中文标题】嵌套 TransactionScope 在测试中失败【英文标题】:Nested TransactionScope fails in tests 【发布时间】:2012-04-26 01:44:38 【问题描述】:

我正在使用 MSTest 通过 mysql 连接器并使用 EntityFramework 4.3 对 MySQL 5.5.19 数据库运行一些自动化测试。

我正在尝试在我的 DB 访问类库中使用 TransactionScope 在需要时执行回滚。此外,在我的测试代码中,我希望在每次测试之前使用TransactionScope 将数据库恢复到已知状态。我使用TestInitializeTestCleanup 方法来实现这一点。那些看起来像这样:

[TestInitialize()]
public void MyTestInitialize()

   testTransScope = new TransactionScope(TransactionScopeOption.RequiresNew);


[TestCleanup()]
public void MyTestCleanup()

   Transaction.Current.Rollback();
   testTransScope.Dispose();

基于初始化函数中TransactionScope 对象的构造,我相信我应该得到一个新的事务范围(没有“环境”存在,所以我相信这个“​​.RequiresNew”不是这里在技术上很重要,因为“.Required”会产生相同的结果。由于我没有指定超时值,它为我提供了默认超时,我理解为 60 秒。我给定的测试有足够的时间运行.

我有一个名为 AddDessert(DessertBiz dessertBizObject) 的函数,部分看起来像这样:

using (var transScope = new TransactionScope(TransactionScopeOption.Required))

   try
   
      // ...
      context.Desserts.Add(dessert);
      context.SaveChanges();
      var dessertId = dessert.Id;
      DoOtherDessertStuff(dessertId, dessertBizObject);
      transScope.Complete();
   
   catch (InvalidOperationException ex)
   
      Console.WriteLine(ex.ToString());
   

我的一个测试调用了这个函数。

由于我在这里指定了TransactionScopeOption.Required,我希望它将使用MyTestInitialize 函数创建的“环境”事务范围。

我的测试被安排使这个DoOtherDessertStuff函数失败并抛出异常,所以对transScope.Complete();的调用不会发生,并且在退出using函数中的using块时自动发生回滚AddDessert函数.

我在这里遇到的问题是,由于它使用在MyTestInitialize 函数中创建的环境事务范围,我的测试Assert 调用不会发生,因为事务范围回滚发生了——至少这是我认为的正在发生。我验证了Transaction.Current.TransactionInformation.StatusTransactionStatus.Aborted,所以我很确定这就是正在发生的事情。

太好了,所以我想我会改变我的 AddDesert 方法,使其看起来与上面完全相同,只是我会嵌套一个事务范围而不是使用环境范围,我的一些 using 行看起来像这样:

using (var transScope = new TransactionScope(TransactionScopeOption.RequiresNew))

这里的目的是我可以嵌套这些事务范围,让我的生产代码中的回滚发生,然后仍然在我的测试代码中检查我的Asserts。

但我发现我收到以下错误:

System.IO.IOException: Unable to read data from the transport connection: 连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立连接失败,因为连接的主机没有响应。

想法?

【问题讨论】:

请发布堆栈跟踪和它发生的代码。 【参考方案1】:

非常好的问题。当您在回滚后在测试方法中引用 datacontext 时,它将不可用。你需要压制它。您不需要指定所需的选项。这是默认选项。

测试方法:

  [TestMethod()]
    public void CreateTestCheckContextCorrectly()
    
        MailJobController target = new MailJobController();

        target.AddDessert("dessert for Omer");
        //With suppress, even if you rollback ambient trans, suppress will ignore ambient trans. You need to reference new context, previous context from controller may be disposed.
        using (var suppressscope = new TransactionScope(TransactionScopeOption.Suppress))
        
            var newdbcontextref = new DbEntities();

            int recordcount = newdbcontextref.StatusDefinitions.Where(x => x.Name == "dessert for Omer").Count();

            Assert.AreEqual(0, recordcount);
        
    

控制器方法:

 public void AddDessert(string dessert)
   
       using (var transScope = new TransactionScope())
       
           try
           
               // ...
               StatusDefinition statusDefinition = new StatusDefinition() Name = dessert;
               db.StatusDefinitions.AddObject(statusDefinition);
               db.SaveChanges();
               Console.WriteLine("object id:"+statusDefinition.StatusDefinitionId);
               throw new Exception("hee hee");
               transScope.Complete();
           
           catch (Exception ex)
           
               Console.WriteLine(ex.ToString());
           
       
   

【讨论】:

以上是关于嵌套 TransactionScope 在测试中失败的主要内容,如果未能解决你的问题,请参考以下文章

复习三层架构

TransactionScope 如何回滚事务?

TransactionScope 与 SQLite 内存数据库和 NHibernate

TransactionScope 是如何工作的?

使用 @IdClass 的 JPA 多对多与额外列在 springTestContextPreparation Hibernate AnnotationException“没有持久的 id 属性”中失

TransactionScope 事务 = new TransactionScope() VS TransactionScope s = context.Connection.BeginTransac