Могу ли я создать макет db-контекста, который я могу добавить, а затем выполнить поиск?

У меня есть простой менеджер документов, который вводится в мой контроллер в проекте asp.net С# MVC. Проект является первым в базе данных, а таблица Document индексируется с помощью documentId, целого числа с автоинкрементом.

Я пытался написать тест, который тестирует следующую реализацию CreateNewDocument, которая после успешного добавления документа ищет его и возвращает новый идентификатор документа.

Проблема в том, что я не могу найти способ издеваться над MyEntityFrameWorkEntities который я могу добавить документ, а затем искать этот документ с помощью linq. Я думаю, что это не работает, потому что _context.Document.Add самом деле ничего не делает.

Мой вопрос заключается в следующем: нужно ли мне изменить мою DocumentManager так что код проверяемый (например, путем замены .First с .FirstOrDefault и возврата к нулю из функции, если это возвращает нуль), или я могу (я) настроить мой издевается по-другому, поэтому я могу оставить DocumentManager как есть и написать тест, который проходит?

public class DocumentManager : IDocumentManager
{
    private readonly MyEntityFrameWorkEntities _context;

    public DocumentManager(MyEntityFrameWorkEntities context)
    {
        _context = context;
    }

    public int CreateNewDocument(int userId)
    {
        var newDocumentGuid = Guid.NewGuid(); 
        var newDocument = new Document
        {
            UserId = userId,
            DateCreated = DateTime.Now,
            DocumentGuid = newDocumentGuid
        };
        _context.Document.Add(newDocument);
        _context.SaveChanges();
        // the .First here doesn't return anything when called from tests
        return _context.Document.First(d => d.DocumentGuid == newDocumentGuid).DocumentId;
    }
}

public partial class MyEntityFrameWorkEntities : DbContext
{
    public MyEntityFrameWorkEntities() : base("name=MyEntityFrameWorkEntities")
    {
    }

    public virtual DbSet<Document> Document { get; set; }
    /* ...etc... */
}

и тестовый класс:

[TestMethod]
public void TestCreateNewDocument()
{
    var mockContext = new Mock<MyEntityFrameWorkEntities>();

    var mockDocumentDbSet = GetQueryableMockDocumentDbSet();

    mockContext.Setup(m => m.Document).Returns(mockDocumentDbSet.Object);

    var documentManager = new DocumentManager(mockContext.Object);

    var newDocId = documentManager.CreateNewDocument(123);

    // This line doesn't get hit as the .First falls over before here
    Assert.AreNotEqual(newDocId, 0);
}

private static Mock<DbSet<Document>> GetQueryableMockDocumentDbSet()
{
    var data = new List<Document> { GetDocument(111, 11), GetDocument(222, 22), GetDocument(333, 33) }.AsQueryable();
    var mockDocumentDbSet = new Mock<DbSet<Document>>();
    mockDocumentDbSet.As<IQueryable<Document>>().Setup(m => m.Provider).Returns(data.Provider);
    mockDocumentDbSet.As<IQueryable<Document>>().Setup(m => m.Expression).Returns(data.Expression);
    mockDocumentDbSet.As<IQueryable<Document>>().Setup(m => m.ElementType).Returns(data.ElementType);
    mockDocumentDbSet.As<IQueryable<Document>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
    return mockDocumentDbSet;
}

private static Document GetDocument(int documentId, int userId)
{
    return new Document
    {
        DocumentId = documentId,
        UserId = userId,
        DateCreated = DateTime.Now.AddDays(-1),
        DocumentGuid = Guid.NewGuid(),
    };
}

Ответ 1

Вы можете настроить свой макет DbSet Add() с обратным вызовом, который добавит элемент в ваш список поддержки:

private static Mock<DbSet<Document>> GetQueryableMockDocumentDbSet()
{
    var data = new List<Document> { GetDocument(111, 11), GetDocument(222, 22), GetDocument(333, 33) };

    var mockDocumentDbSet = new Mock<DbSet<Document>>();
    mockDocumentDbSet.As<IQueryable<Document>>().Setup(m => m.Provider).Returns(data.AsQueryable().Provider);
    mockDocumentDbSet.As<IQueryable<Document>>().Setup(m => m.Expression).Returns(data.AsQueryable().Expression);
    mockDocumentDbSet.As<IQueryable<Document>>().Setup(m => m.ElementType).Returns(data.AsQueryable().ElementType);
    mockDocumentDbSet.As<IQueryable<Document>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
    mockDocumentDbSet.Setup(m => m.Add(It.IsAny<Document>())).Callback<Document>(data.Add);
    return mockDocumentDbSet;

}

Затем ваш последующий вызов First() должен быть в состоянии получить элемент.

Ответ 2

Подумайте о насмешливости на более высоком уровне абстракции. В этом случае рассмотрите издевательство над репозиторием. Вы могли бы пойти еще выше и высмеять саму службу.

Построить тестовую логику бизнес-уровня