Friday, 25 April 2014

Unit Testing a Generic Repository

One of the popular ways to design a data access layer is to use repository pattern. This is quite popular approach taken for ASP.net MVC or a web Api project with entity framework.. While designing repositories we generally prefer to have a generic repository. There are handful common methods required for performing most of the interaction with data base including basic crud operations. However when we need to write unit test methods against methods that use a generic repository, it’s not as straight as in case of normal methods. I share here the way I did it.

Please refer to below code location to check for the implementation in code.

Let us consider the below repository interface:
----------------------------------------------------------------------------------------------------------------
public interface IRepository<TEntity> : IDisposable where TEntity : class
{
        DbSet<TEntity> GetDbSet();

        IQueryable<TEntity> GetAll();

       IQueryable<TEntity> GetEntity(List<string> relatedEntities);

void SetEntityState(object entity, EntityState entityState);

        IQueryable<TEntity> GetFiltered(Expression<Func<TEntity, bool>> filter);

        TEntity GetById(object id);

        IQueryable<TEntity> GetEntity(Expression<Func<TEntity, bool>>filter, List<string> relatedEntities);

        TEntity Create(TEntity entity);

        TEntity Update(TEntity entityToUpdate);

        void Delete(object id);

        void Delete(TEntity entityToDelete);
    }
----------------------------------------------------------------------------------------------------------------
 
Let us have a UnitOfWork class as below :
----------------------------------------------------------------------------------------------------------------
public interface IUnitOfWork : IDisposable
{    
      #region  Methods  

      IRepository<Designation> DesignationRepository { get; }
     
      IRepository<Employee> EmployeeRepository { get; }
     
      void Save();    
     
      #endregion 
}
----------------------------------------------------------------------------------------------------------------

And the UnitOfWork implementation as below :

----------------------------------------------------------------------------------------------------------------
public class UnitOfWork: IUnitOfWork
{
        #region Members 
            
            private bool disposed = false;
            protected EntityContext context;
 
        #endregion
 
        #region Ctor
 
        public UnitOfWork(EntityContext context)   
        {     
            if (context == null)     
            {       
                throw new ArgumentNullException("context wasn't supplied");     
            }      
            this.context = context;   
        }   
       
      #endregion
 
     #region IUnitOfWork Members 
            private  DesignationRepository  designationRepository;
 
            public IRepository<Designation> DesignationRepository   
            { 
                        get
                        {           
                                    if (designationRepository == null)           
                                    {               
                                                designationRepository = new  DesignationRepository(context);           
                                    }           
                                    return designationRepository;       
                        }   
            }       
           
            private  EmployeeRepository  employeeRepository;
            public IRepository<Employee> EmployeeRepository   
            { 
                        get
                        {           
                                    if (employeeRepository == null)           
                                    {               
                                                employeeRepository = new  EmployeeRepository(context);           
                                    }            
                                                return employeeRepository;       
                        }   
            }       
            public void Save()   
            {    
                        try
                         {
                                    this.context.SaveChanges();
                                    }
                                    catch (DbUpdateConcurrencyException concurrencyException)
                                    {
                                    concurrencyException.Entries.Single().Reload();
                                     throw (new Exception("The record was updated already"));
                                    }
                                    catch (Exception ex)
                                    {
                                    throw (ex);
                         }
            }
 
            protected  void Dispose(bool disposing)
             {
                                    if (!this.disposed)
                                    {
                                    if (disposing)
                                     {
                                                 context.Dispose();
                                    }
 
                                    }
                                    this.disposed = true;
             }
 
            public void Dispose()
             {
                                    Dispose(true);
                                    GC.SuppressFinalize(this);
                        }
            #endregion
 }
----------------------------------------------------------------------------------------------------------------
 
Now say we have a repository for Employee that implements IRepository  and lets assume that the repository does not implement any additional method.
 
----------------------------------------------------------------------------------------------------------------
public partial class EmployeeRepository : Repository<Employee>
{
        #region Ctor
        public EmployeeRepository(PeerFeedbackEntities context)
            : base(context) { }
        #endregion      
 }
----------------------------------------------------------------------------------------------------------------
 
Now in order to test business methods that uses UnitOfWork and EmployeeRepository, we need to mock these two classes. In order to have that , we need to simply create a copy of our UnitOfWork class and name it as MockUnitOfWork and also create copy of EmployeeRepository which will look like as below. These MockUnitOfWork and mock EmployeeRepository needs to be in the Unit Test project and MockUnitOfWork needs to use the mock repository.
----------------------------------------------------------------------------------------------------------------
 
public  class EmployeeRepository : IRepository<DE.NetworkArea>
{
        public DbSet<Employee> GetDbSet()
        {
            throw new System.NotImplementedException();
        }
        public System.Linq.IQueryable<Employee> GetAll()
        {
            throw new System.NotImplementedException();
        }
        public System.Linq.IQueryable<Employee> GetEntity(System.Collections.Generic.List<string> relatedEntities)
        {
            throw new System.NotImplementedException();
        }
        public void SetEntityState(object entity, EntityState entityState)
        {
            throw new System.NotImplementedException();
        }
        public System.Linq.IQueryable<Employee> GetFiltered(Expression<System.Func<DE.NetworkArea, bool>> filter)
        {
            throw new System.NotImplementedException();
        }
        public DE.NetworkArea GetById(object id)
        {
            throw new System.NotImplementedException();
        }
        public System.Linq.IQueryable<Employee> GetEntity(Expression<System.Func<DE.NetworkArea, bool>> filter, System.Collections.Generic.List<string> relatedEntities)
        {
            throw new System.NotImplementedException();
        }
        public Employee Create(Employee entity)
        {
            throw new System.NotImplementedException();
        }
        public Employee Update(Employee entityToUpdate)
        {
            throw new System.NotImplementedException();
        }
        public void Delete(object id)
        {
            throw new System.NotImplementedException();
        }
        public void Delete(Employee entityToDelete)
        {
            throw new System.NotImplementedException();
        }
        public void Dispose()
        {
            throw new System.NotImplementedException();
        }
    }

 ----------------------------------------------------------------------------------------------------------------

Now let us create a mock data class for Repository. This class may look like as below :
  ----------------------------------------------------------------------------------------------------------------
public class MockData
{
       public static List<Employee> GetEmployees(){
            List<Employee> employees = new List<Employee>();

            Employee employee = new Employee
            {
                EmployeeId = 101,
                Name = "Sunny",
                DateOfBirth = new DateTime(1972, 11, 23),
                DateOfJoin = new DateTime(2010, 08, 10),
                Address = "S P Road",
                DesignationId = 1,
                Phone = "111-222-3421"
            };
            employees.Add(employee);

            employee = new Employee
            {
                EmployeeId = 101,
                Name = "Jeet",
                DateOfBirth = new DateTime(1976, 01, 12),
                DateOfJoin = new DateTime(2012, 12, 18),
                Address = "Main Road",
                DesignationId = 3,
                Phone = "001-111-1234"
            };
            employees.Add(employee);

            return employees;
        }

 }
----------------------------------------------------------------------------------------------------------------

Now we are ready to return our mock data from the mock repository as shown below:

Let us return from the GetEntity method from the above mock repository:
----------------------------------------------------------------------------------------------------------------

 public System.Linq.IQueryable<Employee> GetEntity(Expression<System.Func<Employee, bool>> filter, System.Collections.Generic.List<string> relatedEntities)
        {         

             var employees = MockData.GetEmployees();
             var result = employees.AsQueryable().Where(filter);
             return result;

              //throw new System.NotImplementedException();
        }


   ----------------------------------------------------------------------------------------------------------------

So now whenever the GetEntity method will be called even with a filter we will get mock but still filtered data.

This way we can actually return mock data from our repository without hitting the database.
To see an actual implementation of the same code click here

 

No comments:

Post a Comment