9

Ho un semplice gestore documenti che viene iniettato nel mio controller in un progetto asp.net C# MVC. Il progetto è database-first e la tabella Document è indicizzata da documentId, un numero intero autoincrementante.Posso creare un contesto dock fittizio che posso aggiungere a, quindi cercare?

Ho provato a scrivere un test che verifica la seguente implementazione di CreateNewDocument, che dopo aver aggiunto correttamente un documento lo cerca e restituisce il nuovo ID documento.

Il problema è che non riesco a trovare un modo per simulare MyEntityFrameWorkEntities cui posso aggiungere un documento e quindi cercare quel documento usando linq. Penso che non funzioni perché il _context.Document.Add deriso non fa davvero niente.

mia domanda è questa: ho bisogno di cambiare la mia DocumentManager in modo che il codice è verificabile (per esempio sostituendo .First con .FirstOrDefault e il ritorno a zero dalla funzione se che restituisce null), o posso (dovrei) insieme il mio mock in modo diverso così posso lasciare il DocumentManager così com'è e scrivere un test che passa?

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

e la classe di test:

[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.CreateNewDocument2(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(), 
    }; 
} 

risposta

25

È possibile impostare il metodo di finto Add del DbSet() con una funzione di callback che aggiungerà la voce nella tua lista di supporto:

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; 

}

tua chiamata successiva a Prima() dovrebbe quindi essere in grado di recuperare l'oggetto.

+1

Questo ora funziona esattamente come speravo, grazie carichi. – jonaglon

+0

Cheers. Questo mi ha letteralmente salvato la vita. – bgs264

+0

Questo non funziona per le chiamate asincrone come "FirstOrDefaultAsync". Il provider per l'origine IQueryable non implementa IDbAsyncQueryProvider. risolto con questo link: https://msdn.microsoft.com/en-us/data/dn314429#async – Max

1

consideri beffardo fuori ad un livello di astrazione più elevato. In questo caso, prendi in considerazione la possibilità di deridere il Respository. Si potrebbe andare anche higer e prendere in giro il servizio stesso.

Construct testable business layer logic

Problemi correlati