1

I'm pretty new to unit testing and I'm having some problems with regards, to unit testing a generic repository in my application. I've implemented the unit of work pattern in my ASP.NET MVC application. My classes look like this:

public class UnitOfWork : IUnitOfWork
{
    private bool disposed = false;

    private IGenericRepository<Shop> _shopRespository;

    public UnitOfWork(PosContext context)
    {
        this.Context = context;
    }

    public PosContext Context { get; private set; }

    public IGenericRepository<Shop> ShopRepository
    {
        get
        {
            return this._shopRespository ?? (this._shopRespository = new GenericRepository<Shop>(this.Context));
        }
    }

    public void SaveChanges()
    {
        this.Context.SaveChanges();
    }

    public void Dispose()
    {
        this.Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                this.Context.Dispose();
            }
            this.disposed = true;
        }
    }
}

public class PosContext : DbContext, IPosContext
{
    public DbSet<Shop> Shops { get; private set; }
}

public class GenericRepository<T> : IGenericRepository<T>
    where T : class
{
    private readonly PosContext context;
    private readonly DbSet<T> dbSet;

    public GenericRepository(PosContext context)
    {
        this.context = context;
        this.dbSet = context.Set<T>();
    }

    public virtual IEnumerable<T> Get(
        Expression<Func<T, bool>> filter = null,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<T> query = this.dbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (var includeProperty in includeProperties.Split
            (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    }

    public virtual T Find(object id)
    {
        return this.dbSet.Find(id);
    }

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

    public virtual void Remove(object id)
    {
        T entityToDelete = this.dbSet.Find(id);
        this.Remove(entityToDelete);
    }

    public virtual void Remove(T entityToDelete)
    {
        if (this.context.Entry(entityToDelete).State == EntityState.Detached)
        {
            this.dbSet.Attach(entityToDelete);
        }
        this.dbSet.Remove(entityToDelete);
    }

    public virtual void Update(T entityToUpdate)
    {
        this.dbSet.Attach(entityToUpdate);
        this.context.Entry(entityToUpdate).State = EntityState.Modified;
    }

I'm using NUnit and FakeItEasy to write my unit tests. In my set up function, I create a UnitIfWork object with a fake PosContext object. I then populate the context with a few Shop objects.

[SetUp]
public void SetUp()
{
    this.unitOfWork = new UnitOfWork(A.Fake<PosContext>());
    this.unitOfWork.ShopRepository.Add(new Shop() { Id = 1, Name = "Test name1" });
    this.unitOfWork.ShopRepository.Add(new Shop() { Id = 2, Name = "Test name2" });
    this.unitOfWork.ShopRepository.Add(new Shop() { Id = 3, Name = "Test name3" });
    this.unitOfWork.ShopRepository.Add(new Shop() { Id = 4, Name = "Test name4" });
    this.unitOfWork.ShopRepository.Add(new Shop() { Id = 5, Name = "Test name5" });
    this.Controller = new ShopController(this.unitOfWork);
}

It works fine when I test the Find-method of the GenericRepository. The correct Shop object is returned and I can assert that it works fine:

    [TestCase]
    public void DetailsReturnsCorrectShop()
    {
      // Arrange
      int testId = 1;
      // Act
      Shop shop = this.unitOfWork.ShopRepository.Find(testId);
      ViewResult result = this.Controller.Details(testId) as ViewResult;
      // Assert
      Shop returnedShop = (Shop)result.Model;
      Assert.AreEqual(testId, returnedShop.Id);
  }

But when I want to test that the Get-method returns all shops from the repository, if I do not give any filter params, I get an empty list back. I can't figure out why?

  [TestCase]
  public void IndexReturnsListOfShops()
  {
      // Arrange
      // Act
      ViewResult result = this.Controller.Index() as ViewResult;
      // Assert
      List<Shop> returnedShops = (List<Shop>)result.Model;
      Assert.AreEqual(5, returnedShops.Count);
  }

The ShopController looks like this:

public class ShopController : Controller
{
  private readonly IUnitOfWork unitOfWork;
  public ShopController(IUnitOfWork unitOfWork)
  {
      this.unitOfWork = unitOfWork;
  }

  // GET: /Shop/
  public ActionResult Index()
  {
      return View(this.unitOfWork.ShopRepository.Get());
  }

  // GET: /Shop/Details/5
  public ActionResult Details(int? id)
  {
      if (id == null)
      {
          return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
      }
      Shop shop = this.unitOfWork.ShopRepository.Find(id);
      if (shop == null)
      {
          return HttpNotFound();
      }
      return View(shop);
  }
}

Can you help me figure out why I get an empty list back from the Get-method?

  • I made a mistake in the original question. It is the Get method that I'm having problems with, and not the Find method as originally stated. I've changed it in the question and added the code for the ShopController. – Steffen Jørgensen Mar 11 '14 at 11:30
  • Interesting. I don't have anything off the top of my head. I notice that there's no `_mediaSpotRepository` member on `UnitOfWork`, so there may be other code that's you're not showing that's interfering, but that seems unlikely. Can you tell us what you've done to debug the problem? Have you run the failing test in isolation? Have you debugged into it? After the first line of `Get`, have you inspected `query`? What type is it? Can you see the elements then? Do you take all the expected branches as you move through Get? – Blair Conrad Mar 11 '14 at 11:48
  • The only member of `PosContext` (your only faked class) that I see any significant interactions with is `Set`, and that appears (correct me if I'm wrong) to come from the non-virtual `DbContext.Set` method (http://msdn.microsoft.com/en-us/library/gg696521(v=vs.113).aspx), so I'm not sure how it would cause problems. Have you tried using a _real_ `PosContext` to compare the behaviour? – Blair Conrad Mar 11 '14 at 11:50
  • I've removed some code for brevity, but missed _mediaSpotRepository. I'ev removed it from the question. The test fails when run alone. When I debug the code, the code takes the expected branch through the Get method, but still doesn't return anything. When i debug `query` I can see that the `local` property contains my 5 Shops, but when I click the Results View, I get a "Children could not be evaluated" message. When I run the application normally against a real database, the call works fine. – Steffen Jørgensen Mar 11 '14 at 12:18
  • Can you tell us what type `query` is when you see it? I may be barking up the wrong tree, but I'm trying to isolate the problem - to see whether it's something on the FakeItEasy side or something else. I wish I could offer more, but I've never used the Entity Framework code, so it's all a mystery to me. – Blair Conrad Mar 11 '14 at 14:20
  • When I open the Watch window it says: System.Linq.IQueryable {System.Data.Entity.DbSet} Thanks for all your help. – Steffen Jørgensen Mar 11 '14 at 14:30
  • So it appears to be a real thing, not a fake thing. I'm currently out. I hope some smart EF people come by. Maybe different tags would attract them. – Blair Conrad Mar 11 '14 at 15:24
  • Thanks a lot for you help. You've still help me with some inspiration with regards to debugging. – Steffen Jørgensen Mar 11 '14 at 15:35
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/49493/discussion-between-blair-conrad-and-steffen-jorgensen) – Blair Conrad Mar 11 '14 at 16:50

0 Answers0