Reputation: 4259
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
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