模拟AsNoTracking实体框架

时间:2014-11-20 11:17:54

标签: c# entity-framework unit-testing mocking moq

如何模拟AsNoTracking方法?
在下面的例子中,DbContext已经注入了服务类。如果我从GetOrderedProducts方法中删除AsNoTracking扩展方法,但是AsNoTracking测试失败,因为它返回null,它工作正常。 我也试图模仿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);
    }
}

问题是AsNoTracking在单元测试中返回null enter image description here

3 个答案:

答案 0 :(得分:45)

查看AsNoTracking()扩展方法的source code

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

由于source(您DbSet<Product>试图模仿)确实是DbQuery(因为DbSet来自DbQuery),它试图调用真实的&#39; (非模拟的)AsNoTracking()方法,它正确地返回null。

尝试模拟AsNoTracking()方法:

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

答案 1 :(得分:3)

你有:

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);

但请记住,扩展方法只是语法糖。所以:

c.Products.AsNoTracking()

真的只是:

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

因此,上面的模拟设置毫无意义。

问题是静态DbExtensions.AsNoTracking(source) method实际对其论证做了些什么。另请参阅the thread What difference does .AsNoTracking() make?

如果您只是从测试类中删除涉及Setup的{​​{1}}会怎样?

提供所有模拟AsNoTracking可能会有所帮助。在这种情况下,您将发现静态方法对它们调用的成员是否可由Moq模拟(即一般意义上的虚方法/属性)。 也许你可以在必要时嘲笑非静态method DbQuery.AsNoTracking

答案 2 :(得分:0)

您可以使用实体框架工作量来模拟AsNoTracking(),也可以使用工作量-Official site for Effort

来模拟Db事务和实体状态。