2016-09-11 4 views
0

У меня есть следующие настройки:метод издевались DbSet бросает NotImplementedException при вызове внутри контроллера

DbContext:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> 
{ 
    public virtual DbSet<Album> Album { get; set; } 

    public ApplicationDbContext() 
     : base("DefaultConnection", throwIfV1Schema: false) 
    { 
    } 

    public static ApplicationDbContext Create() 
    { 
     return new ApplicationDbContext(); 
    } 
} 

Модель:

public class Album 
{ 
    public int AlbumID { get; set; } 

    [StringLength(150)] 
    public string Title { get; set; } 
} 

Контроллер:

public class AlbumController : Controller 
{ 

    ApplicationDbContext db = new ApplicationDbContext(); 

    public AlbumController(ApplicationDbContext injectDb) 
    { 
     db = injectDb; 
    } 

    // POST: Albums/Delete/5 
    [HttpPost, ActionName("Delete")] 
    [ValidateAntiForgeryToken] 
    [Authorize(Roles = "Admin")] 
    public ActionResult DeleteConfirmed(int id) 
    { 
     Album album = db.Album.Find(id); 

     db.Album.Remove(album); 
     db.SaveChanges(); 
     return RedirectToAction("Index"); 
    } 
} 

я написал тест блока с помощью Moq и XUnit проверить функциональность DeleteConfirmed:

public class AlbumsControllerTests 
    { 
     public static Mock<DbSet<T>> MockDbSet<T>(List<T> inputDbSetContent) where T : class 
     { 
      var DbSetContent = inputDbSetContent.AsQueryable(); 
      var dbSet = new Mock<DbSet<T>>(); 

      dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(DbSetContent.Provider); 
      dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(DbSetContent.Expression); 
      dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(DbSetContent.ElementType); 
      dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => inputDbSetContent.GetEnumerator()); 
      dbSet.Setup(m => m.Add(It.IsAny<T>())).Callback<T>((s) => inputDbSetContent.Add(s)); 
      dbSet.Setup(m => m.Remove(It.IsAny<T>())).Callback<T>((s) => inputDbSetContent.Remove(s)); 
      return dbSet; 
     } 

     [Fact] 
     public void DeleteConfirmedTest() 
     { 
      // Arrange 
      var mockAlbumSet = MockDbSet(new List<Album> { }); 

      Mock<ApplicationDbContext> sutDbContext = new Mock<ApplicationDbContext>() { CallBase = true }; 
      sutDbContext.Setup(m => m.Album).Returns(mockAlbumSet.Object); 

      // Check if Album.Remove works inside this test 
      var albumToBeDeleted = new Album() { AlbumID = 1, Title = "TestAlbumName" }; 

      sutDbContext.Object.Album.Add(albumToBeDeleted); 
      Assert.Equal(1, (from a in sutDbContext.Object.Album select a).Count()); 

      sutDbContext.Object.Album.Remove(albumToBeDeleted); 
      Assert.Equal(0, (from a in sutDbContext.Object.Album select a).Count()); 

      // Actual Test 
      sutDbContext.Object.Album.Add(albumToBeDeleted); 
      sutDbContext.Setup(m => m.Album.Find(It.IsAny<int>())) 
      .Returns(albumToBeDeleted); 

      AlbumController sut = new AlbumController(sutDbContext.Object); 

      var output = sut.DeleteConfirmed(1); // Throws NotImplementedException 

      // Assert 
      Assert.Equal(0, (from a in sutDbContext.Object.Album select a).Count()); 
     } 
    } 

Испытание бросает следующее исключение на db.Album.Remove (альбом) в DeleteConfirmed:

System.NotImplementedException: Элемент 'Удалить' не был выполненный по типу 'DbSet 1Proxy' which inherits from 'DbSet 1'. Тест удваивает для «DbSet`1» должен обеспечить реализацию методов и свойства, которые используются.

Как вы можете видеть в теле метода MockDbSet, я установка Удалить метод для моего Мок и он прекрасно работает в тестовом модуле. Можете ли вы объяснить мне, почему он не работает внутри контроллера?

+0

Кажется, вы добавляете некоторую реализацию DbContext в свой контроллер, можете ли вы добавить код, который показывает, какой из них? –

+0

@raderick Я вставляю ApplicationDbContext mock (вы можете проверить его исходный код в верхней части). Инициализация находится в тесте: AlbumController (sutDbContext.Object). Это тот же DbContext, который я использовал в тестовом теле. – Outshined

+1

Тестовый код выглядит странно, в строках после '// Проверяем, работает ли Album.Remove внутри этого теста', вы проверяете свои ложные настройки, что неверно. Ваш 'sut.DeleteConfirmed' также частично проверяет ваши настройки Mock. Я бы посоветовал вам переключить DbSet на IDbSet и использовать 'dbSetMock.Verify (x => x.Удалить (albumToBeDeleted)) ', чтобы проверить, что этот код вызывается без проверки каких-либо деталей реализации. –

ответ

1

Ваш тест будет работать нормально, если вы измените строку:

sutDbContext.Setup(m => m.Album.Find(It.IsAny<int>())) 
      .Returns(albumToBeDeleted); 

To:

mockAlbumSet.Setup(x=>x.Find(It.IsAny<int>())) 
      .Returns(albumToBeDeleted); 

Вы сделали установку для sutDbContext вернуться mockAlbumSet.Object когда sutDbContext.Album называется, но эта линия переопределены вашей установки, чтобы создать новый макет объекта для свойства sutDbContext.Album и создал одну установку для этого макета:

m.Album.Find(It.IsAny<int>())) 
      .Returns(albumToBeDeleted); 

Вот простой тест, который показывает, что вызов настройки для вложенного свойства класса, который был предварительно настроен для возврата Mock.Object, отменит эту собственность с новым Mock.Object:

public interface IParentService 
{ 
    IDependantService Dependant { get; } 
} 

public interface IDependantService 
{ 
    void Execute(); 
} 

[Fact] 
//This test passes 
public void VerifyThatNestedMockSetupGeneratesNewMockObject() 
{ 
    var value = 0; 

    var parentServiceMock = new Mock<IParentService>(); 
    var dependantServiceMock = new Mock<IDependantService>(); 
    dependantServiceMock.Setup(x => x.Execute()).Callback(() => { value = 1; }); 

    parentServiceMock.Setup(x => x.Dependant).Returns(dependantServiceMock.Object); 

    Assert.Same(parentServiceMock.Object.Dependant, dependantServiceMock.Object); 
    parentServiceMock.Setup(x => x.Dependant.Execute()).Callback(() => { value = 2; }); 
    Assert.NotSame(parentServiceMock.Object.Dependant, dependantServiceMock.Object); 

    parentServiceMock.Object.Dependant.Execute(); 

    Assert.Equal(2, value); 
} 
Смежные вопросы