How to handle limitation of in-memory entities to Unit Test class/service which is using EF 6

asp.net-mvc asp.net-web-api c# entity-framework entity-framework-6

Question

I going to unit test a service which is using Entity Framework 6.

Sample scenario, it would have Blog and Post entity, Blog having zero or more Posts. In the service method it would return list of entities which having Blog's Title and Title of first Post of that Blog. It looks like below.

public class BlogService 
{
    private IBloggingContext _context;

    public BlogService(IBloggingContext context)
    {
        _context = context;
    }

    public List<BlogPostSumarry> GeBlogSummary()
    {
        var query = from b in _context.Blogs
                    orderby b.Name
                    select new BlogPostSumarry
                    {
                        BlogTitle = b.Name,
                        PostTitle = b.Posts.FirstOrDefault().Title
                    };

        return query.ToList();
    }
}

}

Unit Test Method

[Test]
    public void GeBlogSummary_WhenMatchFound()
    {
        var post = new List<Post>()
        {
            new Post() {PostId=45, Title="abc"}
        };

        var data = new List<Blog>
        {
            new Blog { Name = "BBB" },
            new Blog { Name = "ZZZ" },
            new Blog { Name = "AAA" },
        }.AsQueryable();

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

        var mockContext = new Mock<BloggingContext>();
        mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);

        var service = new BlogService(mockContext.Object);
        var blogs = service.GeBlogSummary();

        Assert.AreEqual(3, blogs.Count);
        Assert.AreEqual("AAA", blogs[0].BlogTitle);
        Assert.AreEqual("BBB", blogs[1].BlogTitle);
        Assert.AreEqual("ZZZ", blogs[2].BlogTitle);
    }

Unit test I use to mock DbContext and DbSet use in memory data. Problem is in case of blogs doesn’t have any post unit test will giving null reference exception where as in real scenario (Database) it works fine. This is because in a case of in memory it is Linq to Object where as in EF it is Linq to Entity.

For a example if I change the method like below it works UnitTest

public List<BlogPostSumarry> GeBlogSummary()
    {
        var query = from b in _context.Blogs
                    orderby b.Name
                    select new BlogPostSumarry
                    {
                        BlogTitle = b.Name,
//Manually checks for null validation works but any approach without change code for sack of unit test
                        PostTitle = b.Posts.Any() ? b.Posts.FirstOrDefault().Title : null
                    };

        return query.ToList();
    }

Once approach is change linq query for null check but I think it is not good approach. If so any suggestion how can I achieve my test?

1
0
8/24/2018 9:44:53 AM

Popular Answer

In my experience you don't do isolated unit tests for database layer. You do integration tests for this code. You set up a real database (can be LocalDB, it is easy to configure) and run your tests against this test database.

When you try to mock your database, you are testing a mock of your database layer, not the real thing. And you might spend a lot of time tinkering with the mock, but will never get to behave exacty like the database will behave (unless you plan to write a copy of your database).

One of the reason for differerences - LINQ for memory objects just works differently to when it is translated to SQL. You can easily write LINQ that will be perfectly testable with mocks and you'll hvae passable tests. But when executed on real DB the same LINQ will fail because LINQ provider would not know how to translate that to SQL.

Whatever you do with your mock, your memory object will not be the same as SQL requests fired to your DB. And SQL requests is what you are really testing. If you are not testing these, then there is no point in pretending there is a DB behind the storage. Might as well just skip these tests.

1
8/24/2018 2:20:59 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow