Can I create a mock db context which I can add to, then search?

c# entity-framework mocking model-view-controller unit-testing

Question

I have a simple document manager which is injected into my controller in an asp.net c# MVC project. The project is database-first and the Document table is indexed by documentId, an auto incrementing integer.

I have been trying to write a test which tests the following implementation of CreateNewDocument, which after successfully adding a document looks it up and returns the new document id.

The problem is that I can't find a way to mock MyEntityFrameWorkEntities which I can add a document to and then search for that document using linq. I think it doesn't work because the mocked _context.Document.Add doesn't really do anything.

My question is this: can I set up my mocks differently so I can leave the DocumentManager as it is and write a test which passes?

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... */
}

and the test class:

[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
11
3/2/2020 1:30:14 PM

Accepted Answer

You can set up your mock DbSet's Add() method with a callback which will add the item in to your backing List:

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;

}

Your subsequent call to First() should then be able to retrieve the item.

29
2/10/2015 3:45:35 PM

Popular Answer

Consider mocking out at a higher abstraction layer. In this case, consider mocking out the Respository. You could go even higer and mock out the service itself.

Construct testable business layer logic



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow