Entity Framework 6 and Moq4: Is it possible to have a mocked DbSet retain added data for the duration of its scope?

旧时模样 提交于 2019-12-10 10:59:21

问题


Background: - Using EntityFramework 6 - Using Moq v4.2.1402.2112 - Using DbFirst methodology

I have been following the EF6 Moq walkthrough (which can be found here) however; I have been wondering if it is possible to have a mocked DbSet retain the data that is added to it for the duration of it's scope?

For example:

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using Moq; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Linq; 

namespace TestingDemo 
{ 
    [TestClass] 
    public class QueryTests 
    { 
        [TestMethod] 
        public void GetAllBlogs_orders_by_name() 
        { 
            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);
            service.AddBlog("YYY", "http://blogs.msdn.com/yyyy"); 
            var blogs = service.GetAllBlogs(); 

            Assert.AreEqual(4, blogs.Count);    // Fails because whilst the AddBlog is called, and we can verify this, the AsQueryable List doesn't retain the new data
            Assert.AreEqual("AAA", blogs[0].Name); 
            Assert.AreEqual("BBB", blogs[1].Name); 
            Assert.AreEqual("YYY", blogs[2].Name); 
            Assert.AreEqual("ZZZ", blogs[3].Name); 
        } 
    } 
}

Is it possible to have the added data retained so it can be queried later in the test?


回答1:


What you need to do is to use callbacks for all the methods that you expect to modify the collection. A callback in a dynamic proxy is code that runs when a method is called, and has access to the parameters.

Create an ad hoc collection to hold the data (like the data in your sample code). Then implement a callback (1): for each method of the dynamic proxy that can modify the collection. In these callback, modify your ad hoc collection (data). Then in your dynamic proxy, mock the Count method to return the count from this collection (data).

Your ad hoc collection is already created: data.

(2)The method to add a callback to is the one used by your service to add the blog, BlogService.AddBlog I don't use Moq4 so I won't show you the syntax, but look here, in the callbacks section, and use this syntax to add the required callbacks.

The problem with mocking something with many methods and properties and complex behavior like a DbSet<T> is:

  • that you need to implement a lot of callback in your dyanmic proxy: How many methods are that can modify the collections, as stated in (1)?
  • or you need to know details of the implementation of the code under test, to implement only the necessary callbacks, like in (2) Oops! That's ugly. You can implement something that works, but the test can fail because you're not mocking the required methods.

To avoid this problem you have two solutions.

  1. Create an interface to wrap the EF functionality. Mock this interface. This solution limits the number of methods to mock
  2. Use an in-memory database as your backend, so that your testing resembles a real DB and you need to mock nothing. You only need to initialize the in-memory db data for each test. Look here, but don't look only the accepted answer (which BTW is not the best one).


来源:https://stackoverflow.com/questions/24258910/entity-framework-6-and-moq4-is-it-possible-to-have-a-mocked-dbset-retain-added

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!