Scope nested transactions failed in tests

I use MSTest to run some automated tests using MySQL 5.5.19 DB via the MySQL Connector and using EntityFramework 4.3.

I am trying to use TransactionScope in my DB access class library to roll back if necessary. Also, in my test code, I want to use TransactionScope to return the database to a known state before each test. For this, I use the TestInitialize and TestCleanup . They look like this:

 [TestInitialize()] public void MyTestInitialize() { testTransScope = new TransactionScope(TransactionScopeOption.RequiresNew); } [TestCleanup()] public void MyTestCleanup() { Transaction.Current.Rollback(); testTransScope.Dispose(); } 

Based on building the TransactionScope object there in the initialization function, I believe that I should get a new transaction area (there is no existing "ambient", so I believe that this .RequiresNew isn’t technically important here, because ".Required" will be give the same result. Since I do not specify a timeout value, it provides me with a default timeout, which, as I understand it, is 60 seconds. A lot of time for my given test.

I have a function called AddDessert(DessertBiz dessertBizObject) , which in particular looks something like this:

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

And this function is called by one of my tests.

Since I specified TransactionScopeOption.Required here, I expect it to use the "ambient" transaction scope created by the MyTestInitialize function.

My test is designed to make this DoOtherDessertStuff function unsuccessful and throw an exception, so calling transScope.Complete(); does not happen, and rollback occurs automatically when you exit the using block in the AddDessert function.

The problem I have here is that since it uses the volume of the external transaction created in the MyTestInitialize function, my Assert calls are not executed because the transaction scope was rolled back - at least that is what I think is happening. I have confirmed that Transaction.Current.TransactionInformation.Status TransactionStatus.Aborted , so I am sure that this is what is happening.

Great, so I decided to change my AddDesert method to look exactly the same as above, except that I would nest the transaction area rather than use ambient, some of my using lines look like this:

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

The goal was that I could nest these transaction areas, roll back in my production code, and then still check my Assert in my test code.

But I find that I get the following error:

System.IO.IOException: cannot read data from the transport connection: the connection attempt failed because the connected party did not respond properly after some time or the connection was not established because the connected host could not respond.

Ideas?

+6
source share
1 answer

Very good question. When you reference a datacontext inside your test method after the rollback, it will not be available. You need to suppress it. You do not need to specify the required option. This is the default option.

Test Method:

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

Controller Method:

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

Source: https://habr.com/ru/post/913146/


All Articles