简体   繁体   English

模拟AsNoTracking实体框架

[英]Mock AsNoTracking Entity Framework

How do I mock AsNoTracking method? 如何模拟AsNoTracking方法?
In below example, DbContext has injected to the service class.It works fine if I remove AsNoTracking extension method from GetOrderedProducts method, but with AsNoTracking test fails because it returns null. 在下面的示例中,DbContext已注入到服务类中。如果我从GetOrderedProducts方法中删除了AsNoTracking扩展方法,它可以正常工作,但是AsNoTracking测试失败,因为它返回null。 I've also tried to mock AsNoTracking to return proper value but it didn't work. 我也尝试过模拟AsNoTracking以返回适当的值,但是没有用。

public interface IUnitOfWork
{
    IDbSet<TEntity> Set<TEntity>() where TEntity : class;
    int SaveAllChanges();
}

public class Entites : DbContext, IUnitOfWork
{
    public virtual DbSet<Product> Products { get; set; }  // This is virtual because Moq needs to override the behaviour

    public new virtual IDbSet<TEntity> Set<TEntity>() where TEntity : class   // This is virtual because Moq needs to override the behaviour 
    {
        return base.Set<TEntity>();
    }

    public int SaveAllChanges()
    {
        return base.SaveChanges();
    }
}

    public class ProductService
{
    private readonly IDbSet<Product> _products;
    private readonly IUnitOfWork _uow;

    public ProductService(IUnitOfWork uow)
    {
        _uow = uow;
        _products = _uow.Set<Product>();
    }
    public IEnumerable<Product> GetOrderedProducts()
    {
        return _products.AsNoTracking().OrderBy(x => x.Name).ToList();
    }
}

    [TestFixture]
public class ProductServiceTest
{
    private readonly ProductService _productService;

    public ProductServiceTest()
    {
        IQueryable<Product> data = GetRoadNetworks().AsQueryable();
        var mockSet = new Mock<DbSet<Product>>();
        mockSet.As<IQueryable<Product>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<Product>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<Product>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<Product>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
        var context = new Mock<Entites>();
        context.Setup(c => c.Products).Returns(mockSet.Object);
        context.Setup(m => m.Set<Product>()).Returns(mockSet.Object);
        context.Setup(c => c.Products.AsNoTracking()).Returns(mockSet.Object);
        _productService = new ProductService(context.Object);
    }

    private IEnumerable<Product> GetRoadNetworks()
    {
        return new List<Product>
        {
            new Product
            {
                Id = 1,
                Name = "A"
            },
            new Product
            {
                Id = 2,
                Name = "B"
            },
            new Product
            {
                Id = 1,
                Name = "C"
            }
        };
    }

    [Test]
    public void GetOrderedProductTest()
    {
        IEnumerable<Product> products = _productService.GetOrderedProducts();
        List<string> names = products.Select(x => x.Name).ToList();
        var expected = new List<string> {"A", "B", "C"};
        CollectionAssert.AreEqual(names, expected);
    }
}

The problem is AsNoTracking returns null in unit test 问题是AsNoTracking在单元测试中返回null 在此处输入图片说明

Looking at the source code of the AsNoTracking() extension method: 查看AsNoTracking()扩展方法的源代码

public static IQueryable AsNoTracking(this IQueryable source)
{
    var asDbQuery = source as DbQuery;
    return asDbQuery != null ? asDbQuery.AsNoTracking() : CommonAsNoTracking(source);
}

Since source (your DbSet<Product> you're trying to mock) is indeed a DbQuery (because DbSet is deriving from DbQuery ), it tries to invoke the 'real' (non-mocked) AsNoTracking() method which rightfully returns null. 由于source (您要模拟的DbSet<Product> )确实是DbQuery (因为DbSet是从DbQuery ),因此它试图调用“真实”(未经模拟)的AsNoTracking()方法,该方法正确地返回null。

Try to mock the AsNoTracking() method as well: 尝试模拟AsNoTracking()方法:

mockSet.Setup(x => x.AsNoTracking()).Returns(mockSet.Object);

You have: 你有:

context.Setup(c => c.Products).Returns(mockSet.Object);
context.Setup(m => m.Set<Product>()).Returns(mockSet.Object);
context.Setup(c => c.Products.AsNoTracking()).Returns(mockSet.Object);

But remember that extension methods are just syntactic sugar. 但是请记住,扩展方法只是语法糖。 So: 所以:

c.Products.AsNoTracking()

is really just: 真的只是:

System.Data.Entity.DbExtensions.AsNoTracking(c.Products)

therefore your mock setup above is meaningless. 因此,您上面的模拟设置毫无意义。

The question is what the static DbExtensions.AsNoTracking(source) method actually does to its argument. 问题是静态DbExtensions.AsNoTracking(source)方法实际上对其参数做了什么。 Also see the thread What difference does .AsNoTracking() make? 另请参见线程.AsNoTracking()有什么区别?

What happens if you just remove the Setup involving AsNoTracking from your test class? 如果AsNoTracking测试类中删除涉及AsNoTrackingSetup ,会发生什么情况?

It might be helpful to give all your mocks MockBehavior.Strict . 为您提供所有MockBehavior.Strict可能会有所帮助。 In that case you will discover if the members the static method invokes on them, are mockable by Moq (ie virtual methods/properties in a general sense). 在那种情况下,您会发现静态方法对它们调用的成员是否可以被Moq模拟(即,一般意义上的虚拟方法/属性)。 Maybe you can mock the non-static method DbQuery.AsNoTracking if necessary. 如有必要,也许可以模拟非静态方法DbQuery.AsNoTracking

您可以使用实体框架工作量来模拟AsNoTracking(),也可以使用工作量来模拟Db事务和实体状态-工作量的官方网站

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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