简体   繁体   中英

Unit Testing Generic Repository “Find” Method - MSTest, Moq, EF6

I'm fairly new to Unit Testing and Moq, but I'm attempting to setup unit tests for a generic repository using Moq and MSTest. I'm sure there's plenty of things I'm potentially doing incorrectly, but I could really use help with testing the expression based parameters of my generic repository. Specifically, I can't seem to get my mocked repository to return an expected List of objects Here's the code:


Context:

public class OrderContext : DbContext, IDisposable
{
    public virtual DbSet<Order> Orders { get; set; }

    public virtual void Commit()
    {
        base.SaveChanges();
    }

}


Repository Interface / Repository

public interface IRepository<T> where T : class
{
    IEnumerable<T> GetManyBySearch(Func<T, bool> where);
}

public class RepositoryBase<T> : IRepository<T> where T : class
{
    private OrderContext _db;
    private DbSet<T> _dbSet;

    public RepositoryBase(OrderContext db) 
    {
        _db = db;
        _dbSet = _db.Set<T>();
    }

    // Get many records by search
    public virtual IEnumerable<T> GetManyBySearch(Func<T, bool> where)
    {
        return _dbSet.Where(where).AsQueryable();
    }
}


... And in my Unit Test, ideally I would be able to setup a test similar to this one , only I'd like to have my repository return a subset of in-memory object data based on the expression being passed to the GetManyBySearch() method. Currently, my mock repository is returning the entire data set, regardless of the expression being passed . Here's the example:


Unit Test

    [TestMethod]
    [TestCategory("Repository Tests")]
    public void Order_Get_Many_By_Search()
    {

        Mock<OrderContext> mockDb = new Mock<OrderContext>();
        Mock<RepositoryBase<Order>> mockRepo = new Mock<RepositoryBase<Order>>(mockDb.Object);


        var data = new List<Order>()
        {
            new Order { OrderId = 1, OrderNumber = 111, OrderDate = DateTime.Now },
            new Order { OrderId = 2, OrderNumber = 222, OrderDate = DateTime.Now },
            new Order { OrderId = 3, OrderNumber = 222, OrderDate = DateTime.Now }
        }.AsQueryable();

        var expectedResults = data.Where(m => m.OrderNumber == 222);

        var mockSet = new Mock<DbSet<Order>>();
        mockSet.As<IQueryable<Order>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<Order>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<Order>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<Order>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        mockDb.Setup(m => m.Set<Order>()).Returns(mockSet.Object);

        mockRepo.Setup(m => m.GetManyBySearch(It.IsAny<Func<Order, bool>>()))
            .Returns(mockSet.Object.AsQueryable());

        List<Order> results = mockRepo.Object.GetManyBySearch(m => m.OrderNumber == 222).ToList(); // This returns the entire "data" dataset

        mockRepo.Verify(m => m.GetManyBySearch(It.IsAny<Func<Order, bool>>()), Times.Once());

        Assert.AreEqual(2, results.Count()); // This fails :(
    }


Any help would be greatly appreciated. Thanks in advance.

I suspect the test is failing on the line indicated because your sample data has 3 elements and you are asserting that it contains 2:

var data = new List<Order>()
    {
        new Order { OrderId = 1, OrderNumber = 111, OrderDate = DateTime.Now },
        new Order { OrderId = 2, OrderNumber = 222, OrderDate = DateTime.Now },
        new Order { OrderId = 3, OrderNumber = 222, OrderDate = DateTime.Now }
    }.AsQueryable();

// <snip>...

Assert.AreEqual(2, results.Count()); 

However, the bigger issue here is that in your sample, you are actually mocking the method that you're trying to test:

Mock<RepositoryBase<Order>> mockRepo = new Mock<RepositoryBase<Order>>(mockDb.Object);

// <snip>...

List<Order> results = mockRepo.Object.GetManyBySearch(m => m.OrderNumber == 222).ToList();

Because of this, you're not testing any of your production code. Instead, you're just testing that mocked objects will do what you tell them to.

Replace the mocked repository with an actual instance, and inject all of your other mocked objects into it. This will then be a good test that the GetManyBySearch method returns what you'd expect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM