嵌套的TransactionScope在测试中失败

9
我正在使用MSTest通过MySQL Connector和EntityFramework 4.3对MySQL 5.5.19 DB运行一些自动化测试。我试图在我的DB访问类库中使用TransactionScope,在需要时执行回滚。此外,在我的测试代码中,我希望使用TransactionScope在每个测试之前将DB恢复到已知状态。我使用TestInitialize和TestCleanup方法来完成这个任务。这两个方法如下所示:
[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();的调用不会发生,而回滚会在退出AddDessert函数中的using块时自动发生。

我在这里遇到的问题是,因为它使用了在MyTestInitialize函数中创建的“上下文”事务范围,所以我的测试Assert调用没有发生,因为事务范围回滚了 - 至少我认为是这样。我验证了Transaction.Current.TransactionInformation.StatusTransactionStatus.Aborted,所以我相当确定这就是正在发生的事情。

很好,所以我想修改我的AddDesert方法,使其看起来与上面完全相同,只是我将嵌套一个事务范围而不是使用上下文中的一个,所以我的using行看起来像这样:

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

本意是我可以嵌套这些事务范围,让生产代码中的回滚发生,然后仍然可以检查测试代码中的断言。

但我发现我得到了以下错误:

System.IO.IOException: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。

有什么建议吗?


请发布堆栈跟踪和发生问题的代码。 - usr
1个回答

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());
           }
       }
   }

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