简体   繁体   中英

Data access layer throws transaction exceptions when running multiple tests at the same time

I'm unit testing my Data Access Layer (I know it's called integration testing and so on) which is built on Entity Framework. I have above 10 tests, often they're quite broad because single test is checking for instance whole CRUD of some entity. When I run it one by one everything is okay. I've just run one of those tests 7 consequent times with always positive result. But when I run all tests at the same time about half of them throws following exception:

System.Transactions.TransactionAbortedException : The transaction has aborted.

It's very concerning because I'm afraid it could be some problem with frequent calls to DAL.

I'm using xUnit 1.9.2, VisualStudio 2012 (with xUnit test runner) and Entity Framework 5.0.0. DAL connects to an Oracle Database using ODP.NET provider.

EDIT:

My DAL contains following methods for transaction handling:

protected SomeDb _context = new SomeDb(); //It's DbContext
protected TransactionScope _transactionScope;

public void BeginTransaction()
{
    _transactionScope = new TransactionScope();
}

public void Rollback()
{
    _transactionScope.Dispose();
}

On the other hand my test methods look like that:

[Fact]
public void DAL_Entity_CRUD()
{
    #region Arrange

    IDataAccess _dataAccess = new DataAccess();

    //Somecode

    #endregion

    #region Act

    _dataAccess.BeginTransaction();

    //Somecode

    _dataAccess.Rollback();

    #endregion

    #region Assert

    //Somecode

    #endregion
}

However I discovered recently it should be rather:

IDataAccess _dataAccess = new DataAccess();
try
{
    _dataAccess.BeginTransaction();
    //Some code
    _dataAccess.Rollback();
}
catch
{
    _dataAccess.Rollback();
    throw;
}

Am I right?

Anyway my DAL implements IDisposable, so as far as I understand the transaction should be closed while disposing TransactionScope and DbContext at the end of the test method...

~DataAccess()
{
    this.Dispose(false);
}

protected bool Disposed { get; private set; }

public void Dispose()
{
    // Dispose of unmanaged resources.
    this.Dispose(true);
    // Suppress finalization.
    GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
    if (!this.Disposed)
    {
        if (disposing)
        {
            // Perform managed cleanup here.
            _transactionScope.Dispose();
            _context.Dispose();
        }

        // Perform unmanaged cleanup here.

        this.Disposed = true;
    }
}

Look for (and post so we can see) any usage of TransactionScope in your xUnit project.

If I had to guess, I'd say you're wrapping the entire test run in a transaction, rather than wrapping each individual test. Either this "global exception" is happening explicitly due to a transaction created in a fixture setup class (IUseFixture) or it's happening accidently by creating a transaction in the test class constructor and then not disposing it in the test class's disposable method, causing new transactions to nest.

In either case, if one of your tested methods does not call .Complete on a transaction (which from a business standpoint that might be correct), that puts the entire transaction in doubt, causing any later TransactionScope constructors and .Complete methods to fail. That global exception could also be timing out since it is now covering multiple tests. I believe the default is either a minute or 30 seconds.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM