2017-06-22 2 views
0

Ich habe ein Repository-Muster mit einem Zwiebel-Architektur und Entity Framework für den Datenzugriff implementiert, und jetzt möchte ich es mit Moq testen. Ich habe nur eine Frage zu SO gestellt und mit der Antwort bin ich nun etwas verwirrter (die Antwort war gut, aber ich habe ein sehr schlechtes Verständnis davon, wie man sich selbst nach dem Lesen von Dok. Verspotten kann). Was ich tun möchte, ist Test Repository Methode Get(long id). Mein Repository Konstruktor nimmt ein DbContext als Parameter (genannt PrincipalServerContext, so wurde ich vorgeschlagen, den Kontext, um zu verspotten meine Repository testen wir sagen, dass dies mein ist Repository.Wie Setup Mocked Kontext mit Moq?

public class PrincipalServerContext : DbContext 
{ 
    public DbSet<Web_Documents> WebDoc { get; set; } 
    public PrincipalServerContext() 
     : base("name=PrincipalServerDB") 
    { 
     Database.SetInitializer(new Initializer()); 
    } 
} 

Jetzt eine meiner POCOs Web_Documents (EF Einheit):

public class Web_Documents 
{ 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public long IDDocument { get; set; } 

    [Required] 
    [MaxLength(255)] 
    public string NomDocument { get; set; } 

    [Required] 
    public long IDCategorie { get; set; } 

    [ForeignKey("IDCategorie")] 
    public Web_Categories cat { get; set; } 
    [Required] 
    [MaxLength(255)] 
    public string Lien { get; set; } 

    [MaxLength(50)] 
    public string Type { get; set; } 

    public virtual ICollection<Web_Profils> Profils { get; set; } 
} 

Und schließlich, ich benutze POCOs als generische Typen) meine Repository Methode (wohl wissend, dass Repository generisch ist:

public T Get(long id) 
{ 
    ObjectContext objContext = ((IObjectContextAdapter)context).ObjectContext; 
    ObjectSet<T> set = objContext.CreateObjectSet<T>(); 
    IEnumerable<string> keyNames = set.EntitySet.ElementType 
               .KeyMembers 
               .Select(k => k.Name); 
    if (keyNames.Count() > 1) 
     return null; 
    else 
    { 
     string idName = keyNames.ElementAt(0); // For Document would be IDDocument 
     var parameter = Expression.Parameter(typeof(T)); 
     var property = Expression.Property(parameter, idName); 
     var idValue = Expression.Constant(id, id.GetType()); 
     var equal = Expression.Equal(property, idValue); 
     var predicate = Expression.Lambda<Func<T, bool>>(equal, parameter); 
     return entities.SingleOrDefault(predicate); 
     //Returns the corresponding entity to 'id' and 'T' 
    } 
} 

Dies erstellt einen Ausdruck mit entsprechenden ID-Namen, da jede Tabelle einen anderen ID-Namen (Unternehmensrichtlinie) hat.

Von dem, was mir hier gesagt wurde Should this case of Assert.AreSame return true? Ich verstehe, dass ich einen Rückgabetyp für das Mock-Objekt erstellen muss, aber meine Kontextklasse ist so dünn, ich habe keine Methoden oder irgendetwas, nur ein DbSet. Also habe ich versucht, dies als ein Test, aber es macht wohl keinen Sinn, da es versäumt (Ich bin gerade wirklich verloren und es nicht verstehen):

Mock<PrincipalServerContext> moqContext; 
public void IdExists(){ 
    moqContext = new Mock<PrincipalServerContext>(); 
    var set = new Mock<DbSet<Web_Documents>>(); 
    moqContext.Setup(c => c.Set<Web_Documents>()).Returns(set.Object); 
    repoDoc = new Repository<Web_Documents>(moqContext.Object); 
    var testDoc = repoDoc.Get(1L); 
    Assert.AreEqual(testDoc.NomDocument, "Ajouter une catégorie"); 
} 

Sagen wir, ich möchte einen einfachen Test machen, zu finden Wenn die gesuchte ID meinem DB-Eintrag entspricht, wie soll ich das moqContext Objekt festlegen, das ich definieren möchte? In Beispielen sehe ich, dass sie normalerweise Methoden für ihre verspotteten Objekte haben, aber hier keine, also habe ich diese Mocking DbContext for TDD Repository gefunden, die mich diesen Test ausprobieren ließ.

Danke für Ihre Hilfe!

+1

Erwägen Sie, Ihr Repository dünn und verspottet auf dieser Ebene zu halten. Wenn das Repository keine Geschäftslogik hat, dient es als guter Ausgangspunkt für Komponententests. Dies ist meine Rechtfertigung für die Verwendung des Repository-Musters, damit ich mich nicht über einen Kontext lustig machen muss. Verwenden Sie Integrations (End-to-End) -Tests, um zu bestätigen, dass Ihr Code bei einem bekannten Datenzustand wie erwartet funktioniert. Auf diese Weise können Komponententests schnell ausgeführt werden (TDD), während Integrationstests Ihr Ende-zu-Ende-Verfahren abdecken. –

+0

@StevePy Also sagst du, ich brauche meinen Kontext nicht zu verspotten, um diese Methoden zu testen, und mache einfach klassische Komponententests?Für Integrationstests werde ich den Controller in meinem MVC-Projekt testen, der das Repository-Muster verwendet, aber vorerst möchte ich noch sicherstellen, dass alles funktioniert. –

+1

Wenn die Repositorys dünn sind, geben sie einfach Entitäten nach einfachen Kriterien zurück oder speichern/löschen Diese Entitäten profitieren dann nicht viel von einem Komponententest. Zum Beispiel ein "Get" nach ID, es gibt keine Logik, es ist eine Durchleitung zum ORM. Was Sie möglicherweise an einem Repository testen möchten, ist, wenn das Repository untergeordnete Kriterien wie Berechtigungsprüfungen und Ähnliches hinzufügt. Dies sind Dinge, die bei Integrationsläufen abgedeckt werden können. Die wahre Logik liegt in den Controllern und Diensten. Das Mocking des Repository macht diese Klassen viel einfacher zu testen, wo Sie sie häufig testen werden. –

Antwort

0

Hier ist ein Beispiel für die Verwendung der In-Memory-Datenbank.

Zuerst erstellen Sie eine Anfrage eine Instanz der Mock Arbeitseinheit.

[TestMethod] 
public async Task ExampleTest() { 
    //arrange 
    Mock<IUnitOfWork> mockUow = MockUowFactory.Get(nameof(ExampleTest)); 

    //act 
    using (var app = YOURAPP(mockUow.Object)){ 
     app.METHODUNDERTEST(); 
    } 

    //assert 
    ... 
} 

Dann bauen Sie die Scheineinheit der Arbeit auf. Nach dem, was ich gelesen habe, wird der separate Kontext benötigt (einer für das Seeding, einer für das Testen). Die MockEntityFactory gibt nur ein Array von Dummy-Daten zurück, das zum Füllen der dbsets in unserer InMemoryDatabase verwendet wird.

public class MockUowFactory { 

    public static Mock<IUnitOfWork> Get(string dbName) { 

     DbContextOptions<YOUR CONTEXT> options = new DbContextOptionsBuilder<YOUR CONTEXT>() 
      .UseInMemoryDatabase(databaseName: dbName) 
      .Options; 


     using (var seedContext = new YOURCONTEXT(options)) { 

      seedContext.YOURENTITY.AddRange(MockEntityFactory.YOURENTITY); 

      seedContext.SaveChanges(); 
     } 

     var context = new YOURCONTEXT(options); 
     var mockUow = new Mock<IUnitOfWork>(); 
     mockUow.Setup(m => m.Context).Returns(context); 
     mockUow.Setup(m => m.Save()).Returns(() => context.SaveChanges().ToString()); 

     return mockUow; 
    } 
} 

ich diese Einheit der Arbeit durch die notwendigen Schichten passieren dann und brauchen nichts zu tun besonderen meine Produktionscode zu testen.

Verwandte Themen