2017-08-08 1 views
4

Ich bin neu in Moq und möchte es wie einen Backing Store für Daten verwenden - aber ohne die Live-Datenbank zu berühren.Wie gehe ich mit Einheitentesten mit Entity Framework und Moq vor?

Mein Setup ist wie folgt:

  • A UnitOfWork alle Repositories enthält und für den Datenzugriff in der gesamten Anwendung verwendet.
  • Ein Repository stellt einen direkten Hook in ein DbSet dar, das von einem DbContext bereitgestellt wird.
  • Ein DbContext enthält alle DbSets.

Hier ist mein Test so weit:

 // ARRANGE 
     var user = new User() 
     { 
      FirstName = "Some", 
      LastName = "Guy", 
      EmailAddress = "[email protected]", 
     }; 

     var mockSet = new MockDbSet<User>(); 
     var mockContext = new Mock<WebAPIDbContext>(); 

     mockContext.Setup(c => c.Set<User>()).Returns(mockSet.Object); 

     // ACT 
     using (var uow = UnitOfWork.Create(mockContext.Object)) 
     { 
      uow.UserRepository.Add(user); 
      uow.SaveChanges(); 
     } 

     // ASSERT 
     mockSet.Verify(u => u.Add(It.IsAny<User>()), Times.Once()); 

scheint Mein Test erfolgreich zu sein, da es, dass ein Benutzer mit dem Mock DbSet hinzugefügt wurde überprüfen können - aber was muss ich eigentlich tun ist, erhalten diese Daten zurück und führen weitere Aussagen darüber durch (dies ist nur ein Ad-hoc-Test).

Bitte beachten Sie, Test-Frameworks machen mir den Kopf. Außerdem habe ich die Möglichkeit, zu anderen Test-Frameworks zu verschieben, wenn sie einfacher zu bedienen sind.

Vielen Dank.

Update: Hier ist mein Arbeitscode.

Einheit Test

 // ARRANGE 
     var user = new User() 
     { 
      FirstName = "Some", 
      LastName = "Guy", 
      EmailAddress = "[email protected]", 
     }; 

     var mockSet = new MockDbSet<User>(); 
     var mockContext = new Mock<WebAPIDbContext>(); 

     mockContext.Setup(c => c.Set<User>()).Returns(mockSet.Object); 

     // ACT 
     using (var uow = UnitOfWork.Create(mockContext.Object)) 
     { 
      uow.UserRepository.Add(user); 
      uow.SaveChanges(); 
     } 

     // ASSERT 
     mockSet.Verify(u => u.Add(It.IsAny<User>()), Times.Once()); 

     // TODO: Further assertations can now take place by accessing mockSet.BackingStore. 
    } 

MockDbSet

class MockDbSet<TEntity> : Mock<DbSet<TEntity>> where TEntity : class 
{ 
    public ICollection<TEntity> BackingStore { get; set; } 

    public MockDbSet() 
    { 
     var queryable = (this.BackingStore ?? (this.BackingStore = new List<TEntity>())).AsQueryable(); 

     this.As<IQueryable<TEntity>>().Setup(e => e.Provider).Returns(queryable.Provider); 
     this.As<IQueryable<TEntity>>().Setup(e => e.Expression).Returns(queryable.Expression); 
     this.As<IQueryable<TEntity>>().Setup(e => e.ElementType).Returns(queryable.ElementType); 
     this.As<IQueryable<TEntity>>().Setup(e => e.GetEnumerator()).Returns(() => queryable.GetEnumerator()); 

     // Mock the insertion of entities 
     this.Setup(e => e.Add(It.IsAny<TEntity>())).Returns((TEntity entity) => 
     { 
      this.BackingStore.Add(entity); 

      return entity; 
     }); 

     // TODO: Other DbSet members can be mocked, such as Remove(). 
    } 
} 
+0

Zeigen Sie Ihren Code für die Mock db Satz zu halten. Sie müssen nur eine Sammlung erstellen, um als Sicherungsspeicher zu fungieren und die Aufzählungs-DB, die mit der Sicherungsauflistung festgelegt wurde, vorzuspielen. – Nkosi

+0

Entschuldigung. Ich habe meinen Beitrag aktualisiert. MockDbSet ist ungeprüft, da ich mir nicht ganz sicher bin, wie alles zusammenpasst. Ich würde bezweifeln, dass die "abfragbare" Variable so für ein DbSet funktioniert. Wie würde ich eine Sammlung erstellen, die als Backing Store fungiert, indem ich mich in das Mock DbSet einnehme? – Rhonage

Antwort

5

Sie brauchen nur eine Sammlung zu erstellen, die als Hintergrundspeicher und verspotten die Aufzählung db-Set mit der Unterstützung Sammlung zu handeln

public class MockDbSet<TEntity> : Mock<DbSet<TEntity>> where TEntity : class { 
    public MockDbSet(List<TEntity> dataSource = null) { 
     var data = (dataSource ?? new List<TEntity>()); 
     var queryable = data.AsQueryable(); 

     this.As<IQueryable<TEntity>>().Setup(e => e.Provider).Returns(queryable.Provider); 
     this.As<IQueryable<TEntity>>().Setup(e => e.Expression).Returns(queryable.Expression); 
     this.As<IQueryable<TEntity>>().Setup(e => e.ElementType).Returns(queryable.ElementType); 
     this.As<IQueryable<TEntity>>().Setup(e => e.GetEnumerator()).Returns(() => queryable.GetEnumerator()); 
     //Mocking the insertion of entities 
     this.Setup(_ => _.Add(It.IsAny<TEntity>()).Returns((TEntity arg) => { 
      data.Add(arg); 
      return arg; 
     }); 

     //...the same can be done for other members like Remove 
    } 
} 

So, jetzt können Sie eine Liste verwenden, um die Daten

// ARRANGE 
var dataSource = new List<User>(); //<-- this will hold data 
var user = new User() 
{ 
    FirstName = "Some", 
    LastName = "Guy", 
    EmailAddress = "[email protected]", 
}; 

var mockSet = new MockDbSet<User>(dataSource); 
var mockContext = new Mock<WebAPIDbContext>(); 

mockContext.Setup(c => c.Set<User>()).Returns(mockSet.Object); 

// ACT 
using (var uow = UnitOfWork.Create(mockContext.Object)) 
{ 
    uow.UserRepository.Add(user); 
    uow.SaveChanges(); 


    // ASSERT 
    mockSet.Verify(u => u.Add(It.IsAny<User>()), Times.Once()); 
    Assert.IsTrue(dataSource.Contains(user)); //<-- shows mock actually added item 
    Assert.IsTrue(uow.UserRepository.Any(u => u == user)); //<-- show you can actually query mock DbSet 
}