Question/Problem
I am not able to get a passing test because the Generic Repository class this.dbSet = context.Set<T>();
is always null
. As you can see in the code below, I have mocked up the DbSet
and the context. I have also setup the mocked context to return mocked DbSet
. The EnityRepository
constructor takes the mocked context as expected, but this.dbSet = context.Set<T>();
isn't picking up my mocked DbSet
. I'm not sure what I did wrong. Am I not mocking this the right way?
Structure:
IService
Generic Repository
public class EntityRepository<T> : IEntityRepository<T> where T : class
{
internal MyDB_Entities context;
internal DbSet<T> dbSet;
public EntityRepository(MyDB_Entities context)
{
this.context = context;
this.dbSet = context.Set<T>();
}
public virtual T GetByID(object id)
{
return dbSet.Find(id);
}
// more code
}
Interface for Generic Repository
public interface IEntityRepository<T> where T : class
{
IEnumerable<T> Get(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "");
T GetByID(object id);
// more code
}
Unit of Work
public class UnitOfWork : IUnitOfWork, IDisposable
{
MyDB_Entities _context;
public IEntityRepository<Customer> customerRepository { get; set; }
public IEntityRepository<Product> productRepository { get; set; }
public UnitOfWork(MyDB_Entities context)
{
_context = context;
customerRepository = new EntityRepository<Customer>(_context);
productRepository = new EntityRepository<Product>(_context);
}
public void Save()
{
_context.SaveChanges();
}
// more code
}
Interface for Unit Of Work
public interface IUnitOfWork
{
IEntityRepository<Customer> customerRepository { get; set; }
IEntityRepository<Product> productRepository { get; set; }
void Dispose();
void Save();
}
Service
public class SomeService : ISomeService
{
readonly IUnitOfWork _unitOfWork;
public SomeService (IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
// DoSomethingMethod
}
Interface for Service
public interface ISomeService
{
// IDoSomethingMethod
}
Extension
public static class MockDBSetExtension
{
public static void SetSource<T>(this Mock<DbSet<T>> mockSet, IList<T> source) where T : class
{
var data = source.AsQueryable();
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
}
}
Test Class
[TestClass]
public class My_Test
{
Mock<DbSet<Product>> _mockProductDBSet;
Mock<MyDB_Entities> mockContext;
[TestInitialize]
public void TestInitialize()
{
_mockProductDBSet = new Mock<DbSet<Product>>();
mockContext = new Mock<MyDB_Entities>();
mockContext.Setup(s => s.Products).Returns(_mockProductDBSet.Object);
}
[TestMethod]
public void TestMocking()
{
var prod = new Product() { ProductName= "AAA", ProductID = 1 };
_mockProductDBSet.SetSource(new List<Product> { prod });
// more code here (new up the service, then test the service method, etc)
}
}
To install Moq, run the following command in the Package Manager Console Add reference of all other projects in it. For controller testing, we will create Mock service objects and test controller methods. For simplicity, we are going to test CountryController. ? Similarly, you can implement test methods for other controller’s action.
To test your data repository you first need to mock the underlying EntityFrameworkCore layer. This means that you basically need to mock EntityFrameworkCore classes, particularly the your specific entity context which is derived from DbContext and DbSet as a part of the DbContext.
After doing this for the 5 th or 6 th time, I decided to face my fears and get to grips with using the Moq library instead. Here is a simple example, of how you can mock a database repository class using the Moq library.
1) Yes, you shouldn't mock the class you're unit testing. You should mock it's dependencies. 2) Yes, when you do database call in unit test, it will actually call a function of mocked dependency. 3) It depends. EntityFramework and Nhibernate already are a UoW, no need to add extra UoW layer, unless you're using some low level db access method.
Lets say you have a IProuctService
defined as
public interface IProductService {
string GetProductName(int productId);
}
where the concrete implementation depends on IUnitOfWork
public class ProductService : IProductService {
readonly IUnitOfWork _unitOfWork;
public ProductService(IUnitOfWork unitOfWork) {
_unitOfWork = unitOfWork;
}
public string GetProductName(int productId) {
var item = _unitOfWork.productRepository.GetByID(productId);
if (item != null) {
return item.ProductName;
}
throw new ArgumentException("Invalid product id");
}
}
If the method under test is IProductService.GetProductName
, here is an example of test that can be done.
[TestMethod]
public void ProductService_Given_Product_Id_Should_Get_Product_Name() {
//Arrange
var productId = 1;
var expected = "AAA";
var product = new Product() { ProductName = expected, ProductID = productId };
var productRepositoryMock = new Mock<IEntityRepository<Product>>();
productRepositoryMock.Setup(m => m.GetByID(productId)).Returns(product).Verifiable();
var unitOfWorkMock = new Mock<IUnitOfWork>();
unitOfWorkMock.Setup(m => m.productRepository).Returns(productRepositoryMock.Object);
IProductService sut = new ProductService(unitOfWorkMock.Object);
//Act
var actual = sut.GetProductName(productId);
//Assert
productRepositoryMock.Verify();//verify that GetByID was called based on setup.
Assert.IsNotNull(actual);//assert that a result was returned
Assert.AreEqual(expected, actual);//assert that actual result was as expected
}
In this scenario there was no need to mock up DbSet or DbContext as the SUT had no need for the implementations of the dependent interfaces. They can be mocked to be used by the system under test.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With