I'm using MSTest to run some automated tests against a MySQL 5.5.19 DB via the MySQL Connector and using EntityFramework 4.3.
I'm attempting to use TransactionScope
in my DB-accessing class library to perform a rollback when needed. Additionally, in my test code I wish to use TransactionScope
to put the DB back into a known state before each test. I use the TestInitialize
and TestCleanup
methods to accomplish this. Those look like so:
[TestInitialize()]
public void MyTestInitialize()
{
testTransScope = new TransactionScope(TransactionScopeOption.RequiresNew);
}
[TestCleanup()]
public void MyTestCleanup()
{
Transaction.Current.Rollback();
testTransScope.Dispose();
}
Based on the construction of the TransactionScope
object there in the initialize function, I believe I should be getting a new transaction scope (there isn't an "ambient" one existing so I believe this ".RequiresNew" isn't technically important here since ".Required" would produce the same result. Since I don't specify a timeout value, it provides me with the default timeout, which I understand to be 60 seconds. Plenty of time for my given test to run.
I've got a function called AddDessert(DessertBiz dessertBizObject)
which looks, in part, 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've specified TransactionScopeOption.Required
here, I expect that it will use the "ambient" transaction scope created by the MyTestInitialize
function.
My test is arranged to make this DoOtherDessertStuff
function to fail and throw an exception, so the call to transScope.Complete();
doesn't happen and the rollback occurs automatically when exiting the using
block in the AddDessert
function.
The problem I have here is that since it uses the ambient transaction scope created in the MyTestInitialize
function, my test Assert
calls don't happen because the transaction scope rollback happened - at least this is what I think is happening. I verified that Transaction.Current.TransactionInformation.Status
is TransactionStatus.Aborted, so I feel pretty sure this is what's happening.
Great, so I thought I would change my AddDesert
method to look exactly as above except that I would nest a transaction scope rather than using the ambient one, some my using
line looks looks like this:
using (var transScope = new TransactionScope(TransactionScopeOption.RequiresNew))
The intent here was that I could nest these transaction scopes, let the rollback in my production code occur and then still check my Assert
s in my test code.
But what I am finding is that I get the following error:
System.IO.IOException: Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host failed to respond.
Ideas?
Very good question. When you reference datacontext inside your testmethod after rollback, it will not be available. You need to suppress that. You don't need to specify required option. It 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());
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With