0

I have been trying to unity test a query that I've created using a repository pattern I cannot seem to figure this one out. It seems that I must be doing something wrong here.

So I've Created a generic repository that may look something like:

 public interface IRepository<T> where T:class,IEntity, new()
 {
      IQueryable<T> Get();

      T Get(int id);

      T Add(T entity);

      T Update(T entity);

      void Delete(int id);
 }

I also have a Context that may look like:

 public class ApplicationContext:DbContext
 {
      public virtual DbSet<Customer> Customers { get; set; }
      public virtual DbSet<User> Users { get; set; }
 }

so now I, of course, now have a repository that looks like IRepository<Customer> customersRepository.

In my controller, I have a query that may look like:

          var customers = _customerRepository
               .Get()
               .Include(customer => customer.Users)
               .Where(customer=>customer.Status == "active")
               .ToList();

So on to my question, I would like to test this but I am getting an error saying

Value cannot be null

my unit test looks like:

      [TestMethod]
      public void GetCustomerList_ValidParameters_ShouldOnlyReturnItemsWithActiveStatus()
      {

           //Act
           var customers = _customerModel.GetCustomerList(
                _parentId, /*parentId*/
                string.Empty, /*keywords*/
                1, /*page*/
                20, /*count*/
                out var totalCount, /*totalCount*/
                null /*orderBy*/
           );

           //Assert
           Assert.AreEqual(expected:3, actual: customers.Count);
      }

my data setup looks like:

      private void AddDataToRepository()
      {

           var customers = new List<Customer>()
           {
                new Customer{Id = Guid.NewGuid().ToString(), Name = "ABC", Status = "active", Parent_id = _parentId},
                new Customer{Id = Guid.NewGuid().ToString(), Name = "DEF", Status = "canceled", Parent_id = _parentId},
                new Customer{Id = Guid.NewGuid().ToString(), Name = "HIJ", Status = "active", Parent_id = _parentId},
                new Customer{Id = Guid.NewGuid().ToString(), Name = "MNO", Status = "suspended", Parent_id = _parentId},
                new Customer{Id = Guid.NewGuid().ToString(), Name = "QRS", Status = "active", Parent_id = _parentId},
           };

           var users = new List<User>();
           var usersMock = new Mock<DbSet<User>>();
           _customerRepositoryMock.Setup(x => x.Get().Include(It.IsAny(typeof(User))));

           _customerRepositoryMock.Setup(x => x.Get()).Returns(customers.ToDbSet());

      }

How can I mock the users here.

3xGuy
  • 2,235
  • 2
  • 29
  • 51

2 Answers2

1

The issue you ran into is that while you want to mock at the repository level, the code is trying to go too deep in the sense that you were expecting to mock out the separate DBSets so that the code under test would interact with the IQueryable returned expecting a DdSet that relates to other mocked DbSets.

I.e. Given a Customer contains a User: Mock a set of customers expected, mock a set of users, then the calling code somehow knows about them?

Instead, your repository is your boundary as far as the test is concerned. The repository simply returns an IQueryable<Customer> so the mocked repository needs to send back a collection of customers with enough information to satisfy the consumer.

  private void AddDataToRepository()
  {
       var testUser = new User{ UserId = 1, UserName = "Me" };
       var customers = new List<Customer>()
       {

            new Customer{Id = Guid.NewGuid().ToString(), Name = "ABC", Status = "active", Parent_id = _parentId, User = testUser},
            new Customer{Id = Guid.NewGuid().ToString(), Name = "DEF", Status = "canceled", Parent_id = _parentId, User = testUser},
            new Customer{Id = Guid.NewGuid().ToString(), Name = "HIJ", Status = "active", Parent_id = _parentId, User = testUser},
            new Customer{Id = Guid.NewGuid().ToString(), Name = "MNO", Status = "suspended", Parent_id = _parentId, User = testUser},
            new Customer{Id = Guid.NewGuid().ToString(), Name = "QRS", Status = "active", Parent_id = _parentId, User = testUser},
       };

       _customerRepositoryMock.Setup(x => x.Get()).Returns(customers.AsQueryable());
 }

The issue is that your code is expecting to find a user on your customer, so you need to initialize your stub customer to have the user reference. (not expect the calling "Include" to know how to work that out from another set of stubs) The .Include call in the case of the test is effectively ignored. All it does in EF is inform the engine to fetch the related data when it executes the query, it doesn't actually load anything.

I would also recommend reading up on implementing a IDbAsyncQueryProvider for your mocked Queryable results so that you can later utilize async operations against your repository methods. (i.e. ToListAsync() etc.) (Unit-testing .ToListAsync() using an in-memory)

Steve Py
  • 26,149
  • 3
  • 25
  • 43
0

Okay, I hope this helps others, I was not able to actually Mock the include method. So what I ended up doing was making an ICustomerRepository that inherited from IRepository<Customer>. Then I just added a method CustomersWithUsers and mocked that.

3xGuy
  • 2,235
  • 2
  • 29
  • 51