Подтвердить что ты не робот

Макет AsNoTracking Entity Framework

Как мне высмеять метод AsNoTracking?
В приведенном ниже примере DbContext добавлен в класс службы. Он отлично работает, если я удаляю метод расширения AsNoTracking из метода GetOrderedProducts, но с тестом 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 в unit test enter image description here

4b9b3361

Ответ 1

AsNoTracking() исходный код метода расширения AsNoTracking():

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

Так как source (ваш DbSet<Product> который вы пытаетесь DbQuery) действительно является DbQuery (потому что DbSet является производным от DbQuery), он пытается вызвать "реальный" (не AsNoTracking() метод AsNoTracking() который по праву возвращает AsNoTracking().

Попробуйте также AsNoTracking() метод AsNoTracking():

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

Ответ 2

У вас есть:

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) фактически соответствует его аргументу. Также см. поток. Какая разница .AsNoTracking() make?

Что произойдет, если вы просто удалите Setup с участием AsNoTracking из вашего тестового класса?

Возможно, было бы полезно дать все ваши mocks MockBehavior.Strict. В этом случае вы обнаружите, являются ли элементы, которые статический метод вызывает на них, макетируются Moq (т.е. Виртуальные методы/свойства в общем смысле). Возможно, вы можете придумать нестатический метод DbQuery.AsNoTracking, если это необходимо.

Ответ 3

вы можете использовать Entity Framework Effort для насмешки над AsNoTracking(), также вы можете издеваться над транзакциями БД и Entity State, используя Effort - официальный сайт для Effort