Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing With Moq and Repository Pattern

I am new to unit testing and I would like some help. I am using code first with repository pattern. I have a generic repository which contains generic CRUD operation called Generic Repository ( see blow )

 public abstract class GenericRepository<T> where T : class
    {
        private HolidayDatabaseContext _dataContext;
        private readonly IDbSet<T> _dbset;
        protected GenericRepository(IDbFactory databaseFactory)
        {
            DatabaseFactory = databaseFactory;
            _dbset = DataContext.Set<T>();
        }
        protected IDbFactory DatabaseFactory
        {
            get;
            private set;
        }
        protected HolidayDatabaseContext DataContext
        {
            get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); }
        }

        public virtual void Add(T entity)
        {
            _dbset.Add(entity);
        }
        public virtual void Update(T entity)
        {
            _dataContext.Entry(entity).State = EntityState.Modified;
        }
        public virtual void Delete(T entity)
        {
            _dbset.Remove(entity);
        }
        public virtual IEnumerable<T> Enumerable()
        {
            return _dbset.AsEnumerable<T>();
        }

        public virtual IQueryable<T> List()
        {
            return _dbset.AsQueryable<T>();
        }

        public virtual T GetSingleById(int id)
        {
            return _dbset.Find(id);
        }

        public void Save()
        {
            _dataContext.SaveChanges();
        }

    }

I have then inherited it into a User Repository and created some specific methods. see below

public class UserRepository : GenericRepository<User>, IUserRepository
    {
        public UserRepository(IDbFactory databaseFactory)
            : base(databaseFactory) { }


        public int HolidayEntitlement(int userId)
        {
           return HolidayEntitlement(userId, DateTime.Now);
        }
        public int HolidayEntitlement(int userId, DateTime dateTime)
        {
           //Get the User
            var user = this.GetSingleById(userId);

            //Work Total Entitlement
            int entitlement = user.BaseHolidayEntitlement;

            //Years in Service
            entitlement += (dateTime - user.EmploymentStartDate).Days / 365;

            return entitlement;



        }


         public int RemainingHolidayEntitlement(int userId)
        {
            return RemainingHolidayEntitlement(userId, DateTime.Now);
        }

         public int RemainingHolidayEntitlement(int userId, DateTime dateTime)
        {
            return int.MinValue;
        }
    }

I would like to Unit test HolidayEntitlement(int userId, DateTime dateTime) but i need to mock the GetSingleById part in the method

I have written this as a Test but it doesn't compile.

 [TestMethod]
        public void GetHolidayEntitlement25()
        {
            //How to write this Unit test


            //Setup
            var user = new User { AnnualHolidayIncrement = 1, BaseHolidayEntitlement = 25, EmploymentStartDate = new DateTime(2013, 1, 1),Id=1 };

            Mock<UserRepository> mock = new Mock<UserRepository>();
            mock.Setup(m => m.GetSingleById(1)).Returns(user);

            Assert.AreEqual(25, mock.Object.HolidayEntitlement(1));
        }

Any help would be appreciated

like image 830
Mike Ross Avatar asked Nov 04 '13 11:11

Mike Ross


2 Answers

You seem to be saying that you only want to mock part of the interface. When you start encountering this sort of situation it suggests that you are mixing your concerns and probably doing something wrong somewhere.

In this instance your Repository is doing MUCH more than just CRUD and therefore has multiple responsibilities (it should only have one, lookup SOLID programming). You are performing Business logic in the repository and it should not live there! Anything other than simple CRUD operations should be moved out into the Business Logic Layer. I.e. your HolidayEntitlement method calculates something by applying some logic and is therefore NOT a CRUD / repository operation!

So... What you should do is move the business logic bits out into a new class, say UserLogic. Within the UserLogic class you would use an injected IUserRepository to communicate with your repository. In UserLogic that is where you would put your HolidayEntitlement method and it would make a call to IUserRepository.GetSingleById. So, when you then test your UserLogic class you would inject in your mock IUserRepository that would have the stub version of GetSingleById and then you will be back on the right track!

I hope that makes sense / helps?!

--ORIGINAL POST--

P.S. My original post stated that you should mock interfaces, not instances so this still applies and I will leave here for reference:

You should be mocking IUserRepository NOT UserRepository.

That is because UserRepository is an implementation of IUserRepository. You want to say that you are giving it a NEW implementation, i.e. your mock. At the moment you are using the ACTUAL class UserRepository.

like image 51
Belogix Avatar answered Oct 12 '22 13:10

Belogix


Mocking is generally used when you need to supply a fake dependency and in this case you appear to be trying to Mock the System Under Test (SUT) which doesn't really make sense - there's literally no point because your test is not actually telling you anything about the behaviour of UserRepository; all you are doing is testing if you setup your Mock correctly which isn't very useful!

The test code you have given seems to indicate that you want to test UserRepository.HolidayEntitlement.

I would be much more inclined to move functions like that out of your Repository class and into a separate business-logic type class. This way you can test the logic of calculating a user's holiday entitlement in total isolation which is a major principle of unit testing.

In order to test that this function does what it's supposed to do (i.e perform a calculation based on properties of a User) you need to ensure that whatever User instance is being operated on within that function is 100% isolated and under your control - either with a Mock or Fake (Stub) instance of User, in this case Mocks are an excellent choice because you only need to implement the parts of the dependency that your SUT is going to need.

So, what you could do is this:

Define an interface for User

public interface IUser
{
  int BaseHolidayEntitlement{get;set;}
  DateTime EmploymentStartDate {get;set;}
  //other properties for a User here
}

Implement this on your User class

public class User:IUser
{
   //implemement your properties here
   public int BaseHolidayEntitlement{get;set;}
   public DateTime EmploymentStartDate {get;set;}
   //and so on
}

Create a class for User logic

public class UserRules
{
  public int GetHolidayEntitlement(IUser user,DateTime dateTime)
  {
     //perform your logic here and return the result
  }
}

Now your test becomes much simpler and doesn't even need the repository

 [TestMethod]
 public void GetHolidayEntitlement_WithBase25_Returns25()
 {
    //Arrange
    var user = new Mock<IUser>();
    //setup known, controlled property values on the mock:
    user.SetupGet(u=>u.BaseHolidayEntitlement).Returns(25);
    user.SetupGet(u=>u.EmploymentStartDate).Returns(new DateTime(2013,1,1));
    var sut = new UserRules();
    int expected = 25;
    //Act
    int actual = sut.GetHolidayEntitlement(user.Object,DateTime.UtcNow);
    //Assert
    Assert.AreEqual(expected,actual,"GetHolidayEntitlement isn't working right...");
 }
like image 27
Stephen Byrne Avatar answered Oct 12 '22 12:10

Stephen Byrne