Mock DbContext.set<T>.Add() EF6 - c#

I have the following classes (where PilsnerContext is a DbContext class):
public abstract class ServiceBase<T> : IService<T> where T: class, IEntity
{
protected readonly PilsnerContext Context;
protected ServiceBase(PilsnerContext context)
{
Context = context;
}
public virtual T Add(T entity)
{
var newEntity = Context.Set<T>().Add(entity);
Context.SaveChanges();
return newEntity;
}
}
public class ProspectsService : ServiceBase<Prospect>
{
public ProspectsService(PilsnerContext context) : base(context){}
}
And i'm trying to make a unit test of the Add method mocking the context like:
[TestClass]
public class ProspectTest
{
[TestMethod]
public void AddProspect()
{
var mockProspect = new Mock<DbSet<Prospect>>();
var mockContext = new Mock<PilsnerContext>();
mockContext.Setup(m => m.Prospects).Returns(mockProspect.Object);
var prospectService = new ProspectsService(mockContext.Object);
var newProspect = new Prospect()
{
CreatedOn = DateTimeOffset.Now,
Browser = "IE",
Number = "1234567890",
Visits = 0,
LastVisitedOn = DateTimeOffset.Now
};
prospectService.Add(newProspect);
mockProspect.Verify(m=>m.Add(It.IsAny<Prospect>()), Times.Once);
mockContext.Verify(m=>m.SaveChanges(), Times.Once);
}
}
But the assert:
mockProspect.Verify(m=>m.Add(It.IsAny<Prospect>()), Times.Once);
Is failing, I assume is because I'm using Context.set().Add() instead of Context.Prospects.Add() in the Add method but how is the correct way to pass this test?
The exception is:
Expected invocation on the mock once, but was 0 times: m => m.Add(It.IsAny<Prospect>()) No setups configured. No invocations performed.
Thanks in advance.

It looks like you're just missing the setup to return your DbSet:
mockContext.Setup(m => m.Set<Prospect>()).Returns(mockProspect.Object);

I tried your solution Patrick Quirk but I was getting an error telling me that DbContext.Set is not virtual.
I found the solution for that here:
How to mock Entity Framework 6 Async methods?
Creating an interface of the DbContext like
public interface IPilsnerContext
{
DbSet<T> Set<T>() where T : class;
}
That way I could mock it.
Thanks!
This is my first question btw, I'm not sure if I can mark this question as duplicated or something.

Related

System.NotSupportedException : Unsupported expression: x => x

I'm currently trying to moq my Cafe Get method which will throw a ArgumentNullexception if the cafe ID is not found.
Error
System.NotSupportedException : Unsupported expression: x => x.Cafe
Non-overridable members (here: Context.get_Cafe) may not be used in setup / verification expressions.
Is this occurring because moq is unable to handle one of the setup expressions?
Unit test
[Fact]
public async Task GetCafeByIdAsync_Should_Throw_ArgumentNullException()
{
var cafe = new List<Cafe>()
{
new Cafe { Name = "Hanna", CafeId = 1},
new Cafe { Name = "Bella", CafeId = 2 }
}.AsQueryable();
var mockSet = new Mock<DbSet<Cafe>>();
mockSet.As<IQueryable<Cafe>>().Setup(m => m.Provider).Returns(cafe.Provider);
mockSet.As<IQueryable<Cafe>>().Setup(m => m.Expression).Returns(cafe.Expression);
mockSet.As<IQueryable<Cafe>>().Setup(m => m.ElementType).Returns(cafe.ElementType);
mockSet.As<IQueryable<Cafe>>().Setup(m => m.GetEnumerator()).Returns(cafe.GetEnumerator());
var mapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfile());
}).CreateMapper();
var contextMock = new Mock<Context>();
contextMock.Setup(x => x.Cafe).Returns(mockSet.Object); //failing here
var cafeService = new CafeService(contextMock.Object, mapper);
await Assert.ThrowsAsync<ArgumentNullException>(() => cafeService.Get(2));
}
SUT
public async Task<VersionResponse> Get(int cafeId)
{
var cafe = await _context.Cafe.Where(w => w.CafeId == cafeId).ToResponse().FirstOrDefaultAsync();
return new VersionResponse()
{
Data = cafe
};
}
Moq relies on being able to create a proxy class that overrides properties. The Context.Cafe can't be overridden. Try declaring that property virtual.
public virtual IDbSet<Cafe> Cafe { get; set; }
Try setup mock on Set method instead
public virtual DbSet<TEntity> Set<TEntity>() where TEntity : class;
in your case
var contextMock = new Mock<Context>();
contextMock.Setup(x => x.Set<Cafe>()).Returns(mockSet.Object);
I Got a Better way For Integration Tests
You can Use Transaction RollBack for this Type of Tests
in this way you can do what ever you want in Db and then every single movement in Db are back to last state of Before Running Tests
if you using Ef you can add this class to your Test Project and Enjoy it
public abstract class PersistTest<T> : IDisposable where T : DbContext, new()
{
protected T DBContext;
private TransactionScope scope;
protected PersistTest()
{
scope = new TransactionScope();
DBContext = new T();
}
public void Dispose()
{
scope.Dispose();
DBContext.Dispose();
}
}
this class help you to write better integration tests,
your test class should inherited from this abstract class and pass your context class as a instance of DbContext
and one more thing about your Codd you should seperate your Logic and DataAccess from each other
just define a interface Called ICofeeRepository and take your methods in it
Your Code Break the Single Responsibility principle
Your Service Should be Like
public class CoffeService
{
//inject cafe repository ; _repo
public async Task<VersionResponse> Get(int cafeid)
{
var cafe = _repo.FindById(cafeid);
if (cafe == null)
throw Exception("");
//other wise .....
}
}
and your Repository Should be Like
public interface ICafeRepository
{
Cafe Get(int cafeid);
}

How to Create a Unit Test for Adding Items in a Repository?

I have an IUnitOfWork interface that encapsulates my custom repositories. My custom repositories in turn inherit from an IRepository interface.
// The class that I am attempting to unit test
// EmployeeBusiness.cs
private readonly IUnitOfWork _unitOfWork;
public EmployeeBusiness(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public EmployeeDto AddEmployee(EmployeeDto employeeDto)
{
var employee = Mapper.Map<Employee>(employeeDto);
if (employee == null) return null;
_unitOfWork.Employees
.Add(employee);
_unitOfWork.Complete();
return Mapper.Map<EmployeeDto>(employee);
}
// IUnitOfWork interface
public interface IUnitOfWork : IDisposable
{
IEmployeeRepository Employees { get; }
void Complete();
}
// IEmployeeRepository interface
public interface IEmployeeRepository : IRepository<Employee> { }
// IRepository<T> interface
public interface IRepository<TEntity> where TEntity : class
{
void Add(TEntity entity);
// I have added other methods for simplicity
}
I am struggling with unit testing the AddEmployee() method because I am getting this error:
Expected invocation on the mock once, but was 0 times: uow => uow.Employees.Add(Employee)
Configured setups:
IUnitOfWork uow => uow.Employees.Add(Employee)
Performed invocations:
IRepository`1.Add(Employee)
This is my unit test
[SetUp]
public void SetUp()
{
_employeeDto = new EmployeeDto
{
FirstName = "John",
LastName = "Smith",
BirthDate = new DateTime(1965, 12, 31)
};
_employee = new Employee
{
FirstName = "John",
LastName = "Smith",
BirthDate = new DateTime(1965, 12, 31)
};
_unitOfWork = new Mock<IUnitOfWork>();
Mapper.Initialize(cfg =>
{
cfg.AddProfile<EmployeeProfile>();
});
}
[Test]
public void AddEmployee_WhenCalled_AddEmployeeToDatabase()
{
_unitOfWork.Setup(uow => uow.Employees.Add(_employee));
_employeeBusiness = new EmployeeBusiness(_unitOfWork.Object);
_employeeBusiness.AddEmployee(_employeeDto);
_unitOfWork.Verify(uow => uow.Employees.Add(_employee), Times.Once);
_unitOfWork.Verify(uow => uow.Complete(), Times.Once);
}
The reason the test you provided in your answer (copied below) passes when your original fails is because your original was expecting the mock to be called with the specific reference variable you're using (in this case _employee). Perhaps you were expecting Moq and .Verify() to check for equality using .equals() instead of ==?
In the context of your method under test, this is correct and desirable - based on the name of the test you're just looking to test that the method does in fact map your input to an Employee and calls the repository's add method. If you want to make sure that data wasn't lost in the mapping, you could use It.Is(), which takes a function you can use to assert qualities of the input (such as the name matching your expected value).
If you're aiming just to test that the mapping succeeded, you may be interested in Automapper Configuration Validation as a separate test.
[Test]
public void AddEmployee_WhenCalled_AddEmployeeToDatabase()
{
_unitOfWork.Setup(uow => uow.Employees.Add(_employee));
_employeeBusiness = new EmployeeBusiness(_unitOfWork.Object);
var result = _employeeBusiness.AddEmployee(_employeeDto);
//_unitOfWork.Verify(uow => uow.Employees.Add(_employee), Times.Once); <-- This did not work
_unitOfWork.Verify(uow => uow.Employees.Add(It.IsAny<Employee>()), Times.Once); // <-- After changing this to It.IsAny<Employee>() it worked
_unitOfWork.Verify(uow => uow.Complete(), Times.Once);
}
I have managed to get it to work by changing my unit test
[Test]
public void AddEmployee_WhenCalled_AddEmployeeToDatabase()
{
_unitOfWork.Setup(uow => uow.Employees.Add(_employee));
_employeeBusiness = new EmployeeBusiness(_unitOfWork.Object);
var result = _employeeBusiness.AddEmployee(_employeeDto);
//_unitOfWork.Verify(uow => uow.Employees.Add(_employee), Times.Once); <-- This did not work
_unitOfWork.Verify(uow => uow.Employees.Add(It.IsAny<Employee>()), Times.Once); // <-- After changing this to It.IsAny<Employee>() it worked
_unitOfWork.Verify(uow => uow.Complete(), Times.Once);
}
Can anyone please help me understand the difference of using It.IsAny<Employee>() as opposed to the _employee variable?
UPDATE
The explanation can be found at Thorin's answer.
You aren't checking the return value of AddEmployee.
[Test]
public void AddEmployee_WhenCalled_AddEmployeeToDatabase()
{
_unitOfWork.Setup(uow => uow.Employees.Add(_employee));
_employeeBusiness = new EmployeeBusiness(_unitOfWork.Object);
var result = _employeeBusiness.AddEmployee(_employeeDto);
Assert.IsNotNull(result); // <---
_unitOfWork.Verify(uow => uow.Employees.Add(_employee), Times.Once);
_unitOfWork.Verify(uow => uow.Complete(), Times.Once);
}

Unit testing mock DbContext DbSet<> is null

Trying to write unit tests that run against DbContext, in .Net Core. I have performed the same in full framework EF and all works.
The code below represents the minimum code to recreate the issue. I have stripped out anything that isn't implemented
I have the following mocks
// DbContext
public class MockDbContext : DbContext
{
public virtual DbSet<State> State { get; set; }
}
The Repository
// Mock of my repository
public class MockRepository<TModel> : IRepository<TModel> where TModel : class
{
MockDbContext _context;
DbSet<TModel> _dbSet;
public MockRepository(MockDbContext context)
{
_context = context;
// THIS IS NULL
_dbSet = _context.Set<TModel>();
}
public async Task<IList<TModel>> GetAllAsync()
{
return _dbSet.ToListAsync().Result;
}
public void Add(TModel t)
{
_context.Add(t);
}
}
The Unit of work
// Unit of work
public class MockUnitOfWork : IUnitOfWork
{
private Mock<MockDbContext> _mockDbContext;
private IRepository<State> _stateRepo;
public MockUnitOfWork()
{
var mockState = new List<State>() { new State { StateId = 1, Name = "Added" }, new State { StateId = 1, Name = "Deleted" } }.AsQueryable();
var mockStateSet = new Mock<DbSet<State>>();
mockStateSet.As<IQueryable<State>>().Setup(m => m.Provider).Returns(mockState.Provider);
mockStateSet.As<IQueryable<State>>().Setup(m => m.Expression).Returns(mockState.Expression);
mockStateSet.As<IQueryable<State>>().Setup(m => m.ElementType).Returns(mockState.ElementType);
mockStateSet.As<IQueryable<State>>().Setup(m => m.GetEnumerator()).Returns(mockState.GetEnumerator());
_mockDbContext = new Mock<MockDbContext>();
_mockDbContext.Setup(o => o.State).Returns(mockStateSet.Object);
}
public void SaveAsync()
{
_mockDbContext.Object.SaveChanges();
}
public IRepository<State> StateRepository => _stateRepo ?? (_stateRepo = new MockRepository<State>(_mockDbContext.Object));
}
I'm attempting to test some service code, which isn't included here, though the problem can be recreated by calling the following, from a test method.
MockUnitOfWork unitOfWork = new MockUnitOfWork();
var x = unitOfWork.StateRepository.GetAllAsync().Result;
We also have an extension that implements pretty much what can be found Entity Framework Testing with a Mocking Framework under the testing async section. This also results in a null on the DbSet<>
Any idea what i might be missing?
When using Entity Framework Core, the Microsoft.EntityFrameworkCore.InMemory NuGet package provides the infrastructure required for testing scenarios. Your repository can then use an appropriate DbContext, and your unit of work can accept an appropriate repository.
Additional details about testing with the InMemory provider in EF Core can be found at https://learn.microsoft.com/en-us/ef/core/miscellaneous/testing/in-memory.

Cant Mock my Get() function from my repository - MongoDB.Driver 2.2.3

In my unit test I want to test my method that I created for filtering data from MongoDB.
When I try to mock my function like this:
_repo.GetFluent<Location>((Arg.Any<Expression<Func<Location, bool>>>()))
.Returns(x => locations.Where(x.Arg<Expression<Func<Location, bool>>>()).ToList());
It underlines the Returns saying:
Cannot convert lambda expression.
Before when I worked on my simple project using the 2.0.0 MongoDB driver I had no problem mocking my Get() function like this, but now with the new 2.2.3 driver I have an error mocking this. Is there another way?
I've seen that the new driver is using IFindFluent and the older one I used the MongoCursor to get my data.
Should I mock the IFindFluent somehow?
This is my code for the GetFluent() method
public IFindFluent<TEntity, TEntity> GetFluent<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> filter = null) where TEntity : class, new()
{
var collection = GetCollection<TEntity>();
if (filter == null)
{
var emptyFilter = Builders<TEntity>.Filter.Empty;
return collection.Find(emptyFilter);
}
else
{
var filterDefinition = Builders<TEntity>.Filter.Where(filter);
return collection.Find(filterDefinition);
}
}
Yes, you need to mock IFindFluent. Let me show you an example.
I used NUnit and Moq for tests, driver version is 2.2.3.
public interface IRepository
{
IFindFluent<TEntity, TEntity> GetFluent<TEntity>(Expression<Func<TEntity, bool>> filter = null)
where TEntity : class, new();
}
public class LocationService
{
public long CountLocations(IRepository repository)
{
return repository.GetFluent<Location>(location => true).Count();
}
}
[TestFixture]
public class LocationServiceTests
{
[Test]
public void CountLocationsTest()
{
const long LocationCount = 5;
var locationsMock = new Mock<IFindFluent<Location, Location>>();
locationsMock.Setup(x => x.Count(default(CancellationToken))).Returns(LocationCount);
var repoMock = new Mock<IRepository>();
repoMock.Setup(repo => repo.GetFluent(It.IsAny<Expression<Func<Location, bool>>>()))
.Returns(locationsMock.Object);
var locationService = new LocationService();
long result = locationService.CountLocations(repoMock.Object);
Assert.AreEqual(LocationCount, result);
}
}
Good answer from Usein Mambediev. There is an similar example, how to mock IFindFluent with Typemock Isolator without wrapping it into the interface:
[TestMethod, Isolated]
public void TestGet()
{
var target = new ClassUnderTest();
var returnMock = Isolate.Fake.Instance<IFindFluent<Location, Location>>();
int size = 3;
Isolate.WhenCalled(() => returnMock.Count()).WillReturn(size);
Isolate.WhenCalled(() => target.GetFluent(default(Expression<Func<Location, bool>>))).WillReturn(returnMock);
Assert.AreEqual(size, target.GetFluent<Location>(location => true).Count());
}
I've put your method into the public class just in order to test. You only need to change the target.
Good luck!

EF 6 fake db context, can't find the entity

I'm in the middle of covering some of our service classes with unit tests and I have managed to isolate/fake the dbcontext using NSubstitute (following this guide). I have some tests done and working, and things seemed to be alright, but now I can't find an entity I added to the context.
The test code is pretty straightforward:
[Fact]
public void CreateStore_GivenAccount_AccountIsAssignedTheStore()
{
const int accountId = 10;
var account = new Account {Id = accountId};
var fakeContext = new FakeContextBuilder()
.WithAccounts(account)
.Build();
var service = new Service(fakeContext);
const int someProperty = 0;
const string someOtherProperty = "blabla";
service.CreateStore(accountId, someProperty, someOtherProperty);
var storeWasAdded = account.Stores
.Any(store =>
store.SomeProperty == someProperty &&
store.SomeOtherProperty == someOtherProperty);
Assert.True(storeWasAdded);
}
The FakeContextBuilder is a helper class I made for setting up the context (similar methods for other entities):
public class FakeContextBuilder
{
private DbSet<Account> _accountTable;
private static DbSet<TEntity> SetUpFakeTable<TEntity>(params TEntity[] entities) where TEntity : class
{
var fakeTable = Substitute.For<DbSet<TEntity>, IQueryable<TEntity>>() as IQueryable<TEntity>;
var table = entities.AsQueryable();
fakeTable.Provider.Returns(table.Provider);
fakeTable.Expression.Returns(table.Expression);
fakeTable.ElementType.Returns(table.ElementType);
fakeTable.GetEnumerator().Returns(table.GetEnumerator());
return (DbSet<TEntity>) fakeTable;
}
public Context Build()
{
var context = Substitute.For<Context>();
context.Accounts.Returns(_accountTable);
return context;
}
public FakeContextBuilder WithAccounts(params Account[] accounts)
{
_accountTable = SetUpFakeTable(accounts);
return this;
}
}
Service method:
public void CreateStore(int accountID, int someProperty, string someOtherProperty)
{
var account = _context.Accounts.Find(accountID);
account.Stores.Add(new Store(someProperty, someOtherProperty));
}
On the Accounts.Find() row I get null instead of the expected account instance. If I add a breakpoint and look at the context I see that "enumerate results" on Accounts yields no results, but I can see that the provider and enumerator etc are set correctly in non-public members. The fake context builder also works fine in other tests, so my guess is that this is related to the Find() method.
EDIT: I have now confirmed that the Find() method is the culprit since the test passes when doing this instead:
var account = _context.Accounts.Single(act => act.Id == 10);
I still want to use Find() for caching purposes and so on. Can this be configured in the test code somehow? Would hate to mess up the production code for this, since it's really a simple operation.
I have solved the problem. It might not be the most neat solution ever, but it seems to do the trick, and I can't see (at the moment at least) that it would be a maintenance nuisance later on.
I pulled it off by creating a sub-class of DbSet<T> which I imaginatively enough named DbSetWithFind<T>
public class DbSetWithFind<TEntity> : DbSet<TEntity> where TEntity : class
{
private readonly IQueryable<TEntity> _dataSource;
public DbSetWithFind(IQueryable<TEntity> dataSource)
{
_dataSource = dataSource;
}
public sealed override TEntity Find(params object[] keyValues) // sealed override prevents EF from "ruining" it.
{
var keyProperties = typeof (TEntity).GetProperties()
.Where(property => property.IsDefined(typeof (KeyAttribute), true));
return _dataSource.SingleOrDefault(entity =>
keyProperties
.Select(property => property.GetValue(entity))
.Intersect(keyValues)
.Any());
}
}
Then I just modified the Substitute.For() call to use the sub-class, containing my custom implementation of Find().
private static DbSet<TEntity> SetUpFakeTable<TEntity>(params TEntity[] entities) where TEntity : class
{
var dataSource = entities.AsQueryable();
var fakeDbSet = Substitute.For<DbSetWithFind<TEntity>, IQueryable<TEntity>>(dataSource); // changed type and added constructor params
var fakeTable = (IQueryable<TEntity>) fakeDbSet;
fakeTable.Provider.Returns(dataSource.Provider);
fakeTable.Expression.Returns(dataSource.Expression);
fakeTable.ElementType.Returns(dataSource.ElementType);
fakeTable.GetEnumerator().Returns(dataSource.GetEnumerator());
return (DbSet<TEntity>) fakeTable;
}

Categories