Reputation: 2358
I am new to mocking and using Moq. This is my first time trying to Mock the add functionality. I have the mocks setup and it works for read functions, but when I try to unit test an add method, it saves the changes to the db.
How do I mock the add method?
I think I have to Mock my UnitOfWork
class which has the SaveChanges()
method. I want to know how I have to setup the mock to intercept the call to SaveChanges()
and not save to db.
Here are the Mocks I have so far
[SetUp]
public void SetUp()
{
addCount = 0;
IEnumerable<Platform> platformList = new List<Platform>(){
new Platform() { Id = 1, Name = "Unknown"},
new Platform() { Id =2, Name = "Amazon"},
new Platform() { Id = 3, Name = "Prime Pantry"}
};
var platformData = platformList.AsQueryable();
var mockPlatformSet = new Mock<DbSet<Platform>>();
mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.Provider).Returns(platformData.Provider);
mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.Expression).Returns(platformData.Expression);
mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.ElementType).Returns(platformData.ElementType);
mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.GetEnumerator()).Returns(platformData.GetEnumerator());
mockPlatformSet.Setup(m => m.Add(It.IsAny<Platform>())).Callback(() => addCount++);
var mockContext = new Mock<ApplicationDbContext>(){ CallBase = true };
mockContext.Setup(m => m.Platforms).Returns(mockPlatformSet.Object);
mockContext.Setup(m => m.Platforms.Add(It.IsAny<Platform>()));
mockContext.Setup(m => m.Platforms.Add(It.IsAny<Platform>())).Callback(() => addCount++);
unitOfWork = new UnitOfWork(mockContext.Object);
platformRepo = new PlatformRepository(mockContext.Object);
controller = new PlatformController(platformRepo, unitOfWork);
}
Adding UnitOfWork code
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
private bool _isDisposed = false;
public UnitOfWork(DbContext context)
{
_context = context;
}
public void SaveChanges()
{
_context.SaveChanges();
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
_context.Dispose();
}
}
_isDisposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Updated question
I am trying to Unit Test my Create
method of my PlatformController
. In this method, I call the Add
function on the repostiory then the SaveChanges
function of UnitOfWork
. I want to verify that my Platform
object gets added to the DbSet, but 'intercept' the call to SaveChanges()
to that it does not write to db.
How can I do this?
Upvotes: 3
Views: 6641
Reputation: 2358
When I set the CallBase
value of my mockContext
as IUnitOfWork
to false
, that seems to have solved the problem of my unit test writing to the database.
This line of code:
mockContext.As<IUnitOfWork>().CallBase = false;
Here is the code of my Setup
function
[SetUp]
public void SetUp()
{
addCount = 0;
IEnumerable<Platform> platformList = new List<Platform>(){
new Platform() { Id = 1, Name = "Unknown"},
new Platform() { Id =2, Name = "Amazon"},
new Platform() { Id = 3, Name = "Prime Pantry"}
};
var platformData = platformList.AsQueryable();
var mockPlatformSet = new Mock<DbSet<Platform>>();
mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.Provider).Returns(platformData.Provider);
mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.Expression).Returns(platformData.Expression);
mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.ElementType).Returns(platformData.ElementType);
mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.GetEnumerator()).Returns(platformData.GetEnumerator());
mockPlatformSet.Setup(m => m.Add(It.IsAny<Platform>())).Callback(() => addCount++);
var mockContext = new Mock<ApplicationDbContext>(){ CallBase = true };
mockContext.Setup(m => m.Platforms).Returns(mockPlatformSet.Object);
mockContext.Setup(m => m.Platforms.Add(It.IsAny<Platform>()));
mockContext.Setup(m => m.Platforms.Add(It.IsAny<Platform>())).Callback(() => addCount++);
mockContext.Setup(m => m.Set<Platform>()).Returns(mockPlatformSet.Object);
mockContext.As<IUnitOfWork>().CallBase = false;
unitOfWork = new UnitOfWork(mockContext.Object);
platformRepo = new PlatformRepository(mockContext.Object);
controller = new PlatformController(platformRepo, unitOfWork);
}
Upvotes: 5
Reputation: 5103
You're trying to do functional testing here, so it would be wise to have a functional database.
EF can recreate and destroy your database in your setup and teardown methods with a test connection string. This would provide a real functional testing environment for your tests to operate against mimicking the real environment.
Ex:
[TestFixtureSetUp]
public static void SetupFixture() //create database
{
using (var context = new XEntities())
{
context.Setup();
}
}
[TestFixtureTearDown]
public void TearDown() //drop database
{
using (var context = new XEntities())
{
context.Database.Delete();
}
}
[SetUp]
public void Setup() //Clear entities before each test so they are independent
{
using (var context = new XEntities())
{
foreach (var tableRow in context.Table)
{
context.Table.Remove(tableRow);
}
context.SaveChanges();
}
}
Change your connection string in your test project to point to "DbNameTest" and you're gold to use your XEntities class in your tests and it will be clear for setting up and adding test data to interact with.
Upvotes: -1
Reputation: 4450
I'm not sure this is the answer you were looking for, but abstracting the EF DbContext
with a Unit of Work is a terrible idea. The reason is that the context already is a Unit of Work implementation. According to the description of the class in msdn:
Represents a combination of the Unit-Of-Work and Repository patterns and enables you to query a database and group together changes that will then be written back to the store as a unit.
Once you remove the unnecessary abstraction, mocking the context should be fairly easy, especially if you use the newest version of Entity Framework.
Upvotes: 3