Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit test an Entity Framework generic repository using Moq

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:

  • DAL - entity framework, generic repository, unit of work
  • BLL - service, automapper (mapping entity generated classes/objects to business objects)
  • Interface - IService
  • Model - business objects
  • Web - ASP.NET MVC
  • Test - unit test

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)
    }
}
like image 790
NKD Avatar asked Jun 15 '16 18:06

NKD


People also ask

How to implement controller testing in Moq?

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.

How do I test my data repository in Entity Framework?

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.

Is it possible to mock a database repository class using Moq?

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.

Should I mock the class I'm unit testing?

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.


1 Answers

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.

like image 161
Nkosi Avatar answered Sep 29 '22 09:09

Nkosi