嵌套的TransactionScope在测试中失败

时间:2012-04-13 17:02:12

标签: c# mysql entity-framework transactions mstest

我正在使用MSTest通过MySQL Connector和使用EntityFramework 4.3对MySQL 5.5.19数据库运行一些自动化测试。

我正在尝试在我的数据库访问类库中使用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块时会自动进行回滚在AddDessert函数中。

我在这里遇到的问题是,由于它使用MyTestInitialize函数中创建的环境事务范围,我的测试Assert调用不会发生,因为发生了事务范围回滚 - 至少这是我认为发生了什么。我确认Transaction.Current.TransactionInformation.Status TransactionStatus.Aborted ,所以我非常确定这是发生了什么。

很好,所以我想我会改变我的AddDesert方法,看起来完全如上所述,除了我会嵌套一个事务范围而不是使用环境范围,我的using行看起来像这样:

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

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

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

System.IO.IOException:无法从传输连接读取数据:连接尝试失败,因为连接方在一段时间后没有正确响应,或者由于连接的主机无法响应而建立的连接失败。

想法?

1 个答案:

答案 0 :(得分:0)

非常好的问题。在回滚后在testmethod中引用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());
           }
       }
   }