开发者

TransactionScope is not rolling back in MSTest test

开发者 https://www.devze.com 2023-02-08 22:51 出处:网络
I have the following base object in my Tests [TestClass] public abstract class TestClassBase { private TransactionScope _transactionScope;

I have the following base object in my Tests

[TestClass]
public abstract class TestClassBase
{
    private TransactionScope _transactionScope;

    [TestInitialize]
    public virtual void TestInitialize()
    {
        _transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { Timeout = new TimeSpan(0, 10, 0) });
    }

    [TestCleanup]
    public virtual void TestCleanup()
    {
        _transactionScope.Dispose();
    }
}

I have a Test that does the following

[TestMethod]
public void SaveToDatebaseTest(
{
     Program program = new Program();         
     program.Name = "Unit Test Program, I should not exists, when Test is done";
     ProgramManager programManager = new ProgramManager()
     pr开发者_如何转开发ogramManager.Save(program);
}

When I run the test, the records still exists in the database.

I want to avoid using TransactionScope within every test method


You need to change your TestCleanup method, right now you Dispose of the TransactionScope, I believe that is actually doing an implicit commit? (you would think you would need to call Complete() though?), since there are no errors it is not Rolling back the transaction.

try this

[TestCleanup]
public virtual void TestCleanup()
{
    // using System.Transactions;
    Transaction.Current.Rollback();
    _transactionScope.Dispose();
}


i know this question is kind of old, but it may help out someone who might/will be struggling on this in the future like i just did earlier. so if you're using

  • Entity Framework
  • Nunit (v3)

the code below won't work.

class A
{
     private TransactionScope _trans;

     [SetUp]
     public void setup()
     {
        _trans = new TransactionScope();
     }

     [TearDown]
     public void done()
     {
        if(_trans != null)
          _trans.Dispose();
     }

     [Test]
     public void doSomeDbWrite()
     {
         //your code to insert/update/delete data in db
     }
}

I have tried (and doesn't work) creating the TransactionScope before creating the DB Context or the other way around. I think it has something to do with EF itself which i suppose is encapsulated in their own transactions or whatever. i did not dig deeper on that part. anyway, here's how i did it with EF and transactions to ensure that your unit test DB is clean after the unit test is done.

class A
{
     private DbContext_DB;
     private DbContextTransaction _trans;

     [SetUp]
     public void setup()
     {
        DB = new DbContext();//create your db context
        _trans = DB.Database.BeginTransaction();
     }

     [TearDown]
     public void done()
     {
        _trans.Rollback();
        DB = null;
     }
}

Hopefully it will help others searching for this at this time :-)


In .NET Core async Task [TestInitialize] methods are unsupported and will result in the behaviour you describe.

Async [TestInitialize] methods appear to run in a different thread context to the [TestMethod] itself, even if TransactionScopeAsyncFlowOption.Enabled is set in the TransactionScope's options. Transaction.Current will be null in the test method and any changes will not be rolled back when [TestCleanup] disposes the TransactionScope.

Unfortunately there are no plans to support async flow in TestInitialize methods despite this working in older versions of .NET (.NET Framework 4.8 at least).

The only work around at present is to make the [TestInitialize] method non-async and .Wait() / .Result your async calls but please upvote the GitHub issue above to show support for this feature.


If you are using VS2005 or VS2008 (not sure about 2010), you can try MSTestExtensions for automatic database transaction rollback after tests methods are completed. I've used it with MS SQLServer and it's Distributed Transaction Coordinator service running.

I know this may not be exactly what you are looking for, but it may be of help to your situation.


I think what you are trying to achieve here is to create a generic Test class that implements the initialize and cleanup so you don't have to repeat that code for every test, is that correct? Are you even sure that those methods are even called? Be aware that you can't subclass [TestInitialize] and [TestCleanup], so you would have to call those methods from the sublcass. (See https://stackoverflow.com/a/15946140/1638933).


I have the same issue but I used the following code to fix the issue Maybe it can help you:

    private readonly TransactionScope _scope;

    public MyTests()
    {
        var options = new TransactionOptions()
        {
            IsolationLevel = IsolationLevel.Snapshot
        };

        _scope = new TransactionScope(TransactionScopeOption.Required, options,  TransactionScopeAsyncFlowOption.Enabled);
      }

      [TestCleanup]
      public void Cleanup()
      {
          _scope.Dispose();
      }
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号