Question

I have implemented UnitOfWork and GenericRepository following some tutorials.

I have IEFDbContext/EFDbContext class that takes care about Database, my IUnitofWork is as follows...

public interface IUnitOfWork : IDisposable
{
    IGenericRepository<TEntity> GetRepository<TEntity>() where TEntity : class;
    void Save();
}

And IGenericRepository as follows

public interface IGenericRepository<T> where T : class
{
    IQueryable<T> Get();
    T GetByID(object id);
    void Add(T entity);
    void Delete(T entity);
    void DeleteAll(IEnumerable<T> entity);
    void Update(T entity);
    bool Any();
}

My controller is as follows...

public class ProjectController : Controller
{
    private IGenericRepository<Project> ProjectRepo { get; set; }

    private IUnitOfWork _unitOfWork { get; set; }

    public ProjectController(IUnitOfWork uow)
    {
     _unitOfWork = uow;
     ProjectRepo = _unitOfWork.GetRepository<Project>();
    }
}

My create action is as follows

[HttpPost]
public ActionResult Create(AddProjectModel model)
{
    if (ModelState.IsValid)
    {
        ProjectRepo.Add(newProject);
        _unitOfWork.Save();
    }
 }

Everything works when I run the application, I am aware about why to use IuitofWork and GenericRepository, and that is why I am not creating IProjectRepository and then injecting that here...

My question is with unit testing this action.

I have created MockGenericRepository and MockUnitofWork in my Test project as follows...

public class MockUnitOfWork<TContext> : IUnitOfWork where TContext : class, new()
{
    private TContext _ctx;
    private Dictionary<Type, object> _repositories;

    public MockUnitOfWork()
    {
        _ctx = new TContext();
        _repositories = new Dictionary<Type, object>();
    }

    public IGenericRepository<TEntity> GetRepository<TEntity>() where TEntity : class
    {
        if (_repositories.Keys.Contains(typeof(TEntity)))
        {
            return _repositories[typeof(TEntity)] as IGenericRepository<TEntity>;
        }

        var entityName = typeof(TEntity).Name;
        var prop = _ctx.GetType().GetProperty(entityName);
        MockRepository<TEntity> repository = null;
        if (prop != null)
        {
            var entityValue = prop.GetValue(_ctx, null);
            repository = new MockRepository<TEntity>(entityValue as List<TEntity>);
        }
        else
        {
            repository = new MockRepository<TEntity>(new List<TEntity>());
        }
        _repositories.Add(typeof(TEntity), repository);
        return repository;
    }

    public void SetRepositoryData<TEntity>(List<TEntity> data) where TEntity : class
    {
        IGenericRepository<TEntity> repo = GetRepository<TEntity>();

        var mockRepo = repo as MockRepository<TEntity>;
        if (mockRepo != null)
        {
            mockRepo._context = data;
        }
    }

    public void Save()
    {

    }

    public void Dispose()
    {
    }
}

And MockGenericRepository as follows

public class MockRepository<T> : IGenericRepository<T> where T : class
{
    public List<T> _context;

    public MockRepository(List<T> ctx)
    {
        _context = ctx;
    }

    public IQueryable<T> Get()
    {
        return _context.AsQueryable();
    }

    public T GetByID(object id)
    {
      //  return _context.Find(s => s.Id ==  id).SingleOrDefault();
        throw new NotImplementedException();
    }

    public virtual void Add(T entity)
    {
        _context.Add(entity);
    }

    public virtual void Delete(T entity)
    {
        _context.Remove(entity);
    }

    public virtual void DeleteAll(IEnumerable<T> entity)
    {
        _context.RemoveAll(s => s == entity);
    }

    public virtual void Update(T entity)
    {
        var entry = _context.Where(s => s == entity).SingleOrDefault();
        entry = entity;
    }

    public virtual bool Any()
    {
        return _context.Any();
    }
}

My ProjectControllerTest is as follows...

public class ProjectControllerTest
{
    private readonly List<ALCProject> _projectsList;
    private readonly IUnitOfWork _mockU = new MockUnitOfWork<EFDbContext>();

    private ProjectController GetControllerObject()
    {
        foreach (var project in _projectsList)
        {
            _mockU.GetRepository<Project>().Add(project);
        }

        var controller = new ProjectController(_mockU);
        return controller;
    }

[Fact]
public void TestCreateProject()
{
        var controller = GetControllerObject();
        var result = controller.Create(new AddProjectModel());
        Assert.Equal(_mockU.GetRepository<Project>().Get().Count(),4);
}

The issue I have is that my test does passes but when I look inside _mockU.GetRepository().Get() I can see that a new project is added but the "ID" field is 0, I understand the reason for this is because my MockGenericRepsoiotry has context define as public List _context; and that why its just adding new project in the list.

Can someone guide me how can I make it to generate new ID I think I have to Fake EFDbContext but I dont know how ???

Was it helpful?

Solution

The Project has an Id of 0 because nothing is setting it to anything else! It is most likely the Id property of the entity is mapped to an identity column in your database (SQL Server as a guess). When DbContext.Save() is called, it performs an insert operation in SQL. SQL Server is generating a new unique Id value for that row, returning this back to Entity Framework. EF then populates the value back into the object that it was persisting.

Your mock unit of work doesn't actually call DbContext.Save(), despite the fact that it is using the EF context as the data store for the repositories that it returns. You could enhance it by putting code in your MockUnitOfWork<T>.Save() method to provide a number to any Id property that has a value of zero in any of the repositories.

Or, as you say, this is a unit test so perhaps you shouldn't care.

I cannot quite see why your unit of work mock is looking at the Entity Framework context at all. Why not always return a repository based on a List<TEntity>?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top