Vivekh
Vivekh

Reputation: 4259

Unit Testing Entity Framework with ms unit test framework

I am using ms unit test to unit test my project in which I'm using Entity Framework. I am able to mock it using the following example in Msdn But I have a scenario where I need to use transactions in entity framework In a following way

Var tran = testdbcontext.Database.BeginTransaction()

and the test fails as database is null for testdbcontext.

I am wondering if we can mock it somehow to get this working

Upvotes: 2

Views: 931

Answers (1)

Nkosi
Nkosi

Reputation: 246998

Using the linked MSDN article for reference you will notice that they have abstracted the DbContext. Using that same thinking you should also abstract the creation of the Transaction.

First create an abstraction of the expected functionality for the transaction object.

/// <summary>
/// Wraps access to the transaction object on the underlying store connection
/// </summary>
public interface IDbContextTransaction : IDisposable {
    /// <summary>
    ///  Commits the underlying store transaction
    /// </summary>
    void Commit();
    /// <summary>
    /// Rolls back the underlying store transaction
    /// </summary>
    void Rollback();
}

This mirrors the functionality you want from the database transaction.

For production you can have an implementation like this that wraps the actual object you want to use.

public class DbContextTransactionWrapper : IDbContextTransaction {
    private DbContextTransaction dbContextTransaction;

    public DbContextTransactionWrapper(DbContextTransaction dbContextTransaction) {
        this.dbContextTransaction = dbContextTransaction;
    }

    public void Commit() {
        dbContextTransaction.Commit();
    }

    public void Rollback() {
        dbContextTransaction.Rollback();
    }

    public void Dispose() {
        if(dbContextTransaction != null) {
            dbContextTransaction.Dispose();
            dbContextTransaction = null;
        }
    }
}

The DbContext abstraction would include the ability to create transactions...

public interface IStoreAppContext : IDisposable {
    DbSet<Product> Products { get; }
    int SaveChanges();
    void MarkAsModified(Product item);
    IDbContextTransaction BeginTransaction();  
}

and the implementation would use the wrapper

public class StoreAppContext : DbContext, IStoreAppContext
{
    public StoreAppContext() : base("name=StoreAppContext")
    {
    }

    public DbSet<Product> Products { get; set; }

    public void MarkAsModified(Product item)
    {
        Entry(item).State = EntityState.Modified;
    }

    public IDbContextTransaction BeginTransaction() {
        return new DbContextTransactionWrapper(Database.BeginTransaction());
    }
}

That way, in your unit tests you can mock the creation of a transaction via a mocking framework or fake implementation and you will call it directly on the abstracted DbContext.

Assuming testdbcontext is of type IStoreAppContext then your call would look like...

IDbContextTransaction tran = testdbcontext.BeginTransaction();

this will give you access to tran.Commit() and tran.Rollback() which are defined on the interface and would allow for easy testing.

Upvotes: 1

Related Questions