7

I'm not sure that this is possible, but I'm trying to unit test a repository that uses a DbSet. I thought the easiest solution would just be to make an Enumerable, and replace the DbSet with that, this is my attempt.

I'm using C#, EntityFramework, XUnit, and Moq

[Fact]
public void SomeTest() 
{
    //arrange
    var mockContext = new Mock<MyDbContext>();
    var mockData = new List<Person>
        {
            new Person { Name = "Jim", Age = 47 }
        };
    mockContext.Setup(db => db.Persons).Returns((DbSet<Person>)mockData.AsQueryable());

    var repo = PersonRepository(mockContext.Object); 

    //act
    var result = repo.GetByFirstName("Jim");

    //assert
    //do some assertion
}

The error that gets thrown is it can't convert type EnumerableQuery to a DbSet on the mockContext.Returns statement.

Here is something similar to what the interfaces look like.

PersonRepository.cs

public class PersonRepository: EFRepository<Person>, IPersonRepository
{
    public PersonRepository(DbContext dbContext) : base(dbContext)
    {
    }

    public IQueryable<Link> GetByFirstName(string name)
    {
        return DbSet.Where(p => p.FirstName == name);
    }
}

IPersonRepository.cs

public interface IPersonRepository: IRepository<Person>
{
    IQueryable<Person> GetByFirstName(string name);
}

IRepository.cs

public interface IRepository<T> where T : class 
{
    IQueryable<T> GetAll();
    T GetById(int id);
    void Add(T entity);
    void Delete(T entity);
    void Delete(int id);
    void Update(T entity);
}

EFRepository.cs

public class EFRepository<T> : IRepository<T> where T : class
{
    protected DbContext DbContext { get; set; }
    public IDbSet<T> DbSet { get; set; }

    public EFRepository(DbContext dbContext)
    {
        if (dbContext == null)
            throw new ArgumentNullException("dbContext");

        DbContext = dbContext;
        DbSet = DbContext.Set<T>();
    }
    ...
}
Kyle Gobel
  • 5,530
  • 9
  • 45
  • 68
  • If you have a look at the definition of DbSet you will see that it inherits from IEnumerable, so you may need to do the abstraction at the interface (IEnumerable) level. – acarlon Sep 09 '13 at 04:50
  • It also inherits from IQueryable and IEnumerable. – acarlon Sep 09 '13 at 04:58

1 Answers1

1

What you can do is to move the Linq expression from your repository into the business logic and mock the repository instead.

public interface IPersonRepository : IRepository<Person>
{
    IQueryable<Person> GetAll { get; }
}

public class PersonRepository : EFRepository<Person>, IPersonRepository
{
    // ...

    public IQueryable<Person> GetAll
    {
        get { return DbSet; }
    }
}

public class SomeBusinessLogicClass
{
    private readonly IPersonRepository _people;

    public SomeBusinessLogicClass(IPersonRepository people)
    {
        _people = people;
    }

    public IEnumerable<Person> GetByFirstName(string name)
    {
        return _people.GetAll.Where(p => p.FirstName == name);
    }
}

Now rewrite your test to validate the business logic.

[Fact]
public void SomeTest() 
{
    //arrange
    var mockRepository = new Mock<IPersonRepository>();
    var mockData = new List<Person>
        {
            new Person { Name = "Jim", Age = 47 }
        };
    mockRepository.Setup(x => x.GetAll).Returns(mockData);

    var bl = new SomeBusinessLogicClass(mockRepository.Object); 

    //act
    var result = bl.GetByFirstName("Jim");

    //assert
    //do some assertion
}
Torbjörn Kalin
  • 1,976
  • 1
  • 22
  • 31
  • 1
    I updated the question with some more code. I don't quite understand how I would go about doing what you're saying. Saying basically to expose the data source for my repository as a Property of type IQueryable? In my normal implementation connect that to the DbSet, and for testing, just throw in my own mocked data source? Are there any downfalls of doing that? – Kyle Gobel Sep 09 '13 at 22:42
  • I've completely rewritten the answer based on your added code. – Torbjörn Kalin Sep 10 '13 at 05:37