2017-01-26 5 views
2

Я использую Entity Framework и имею общий метод репозитория, который позволяет мне запрашивать DbSet, а также включать свойства навигации. Я пытаюсь написать единичный тест для некоторого кода, который использует этот кусок кода, и мне нужно высмеять его для модульного теста. Я использую Moq.Moq - издевается над сложным методом репозитория - объект списка не возвращается

Вот метод репозитория - это метод, который позволяет мне запрашивать с использованием выражения, а также включать необходимые свойства навигации, которые я хочу. Я видел эту модель в EF Джули Лерман на курсе Enterprise на Pluralsight.

public IEnumerable<TEntity> FindByInclude(Expression<Func<TEntity, bool>> predicate, 
              params Expression<Func<TEntity, object>>[] includeProperties) 
{ 
    var query = GetAllIncluding(includeProperties); 
    IEnumerable<TEntity> results = query.Where(predicate).ToList(); 
    return results; 
} 

private IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] includeProperties) 
{ 
    IQueryable<TEntity> queryable = DbSet.AsNoTracking(); 

    return includeProperties.Aggregate 
     (queryable, (current, includeProperty) => current.Include(includeProperty)); 
} 

Вот пример того, как я называю, используя этот метод в моем коде (я только с указанием соответствующей части методы):

public ApiResult DeleteLocation(int id) 
{ 
    var location = _locationRepository 
     .FindByInclude(l => l.Id == id, l => l.LocationRegions, l => l.Pools) 
     .Single(); 

Так что этот запрос вернет единый Location объект по идентификатору, который я прошел, и связанные коллекции LocationRooms и Staff.

Как настроить Moq для метода FindByInclude? Вот что у меня есть для моей модульного тестирования макета установки:

var mockLocationRepository = new Mock<ILocationRepository>(); 
var location = new Location {Id = 1,Name = "LocationName", LocationRooms = new List<LocationRoom>(), Staff = new List<Staff>()}; 
mockLocationRepository.Setup(r => r.FindByInclude(l => l.Id == It.IsAny<int>(), l => l.LocationRooms, l => l.Staff)) 
      .Returns(() => new List<Location> { location }); 

Из кода настройки MOq показан здесь - я думаю, что я должен получить обратно список 1 места - объект местоположения Я указанный с идентификатором 1. Однако, когда я запускаю свой модульный тест и нажимаю этот код, метод установки для FindByInclude возвращает пустой список. И поэтому, когда код в методе DeleteLocation получает удар, и вызывается метод Single(), я получаю сообщение об ошибке «элемент не содержит последовательности».

Я думаю, проблема в том, что у меня что-то не так с синтаксисом с установкой Moq для метода FindByInclude, но не уверен, что не так.

ответ

2

Слишком долго комментарий так что добавление в ответ

оно выражения. Сначала попробуйте более общую настройку выражения и посмотрите, работает ли она.

var location = new Location { 
    Id = 1, 
    Name = "LocationName", 
    LocationRooms = new List<LocationRoom>(), 
    Staff = new List<Staff>() 
}; 
mockLocationRepository 
    .Setup(m => m.FindByInclude(It.IsAny<Expression<Func<TEntity, bool>>>(), It.IsAny<Expression<Func<TEntity, object>>[]>()) 
    .Returns(() => new List<Location> { location }); 
+0

Спасибо Nkosi - это то, что мне нужно :) – user1750537

4

В качестве альтернативы @ ответ Nkosi, то как насчет того, чтобы не использовать Moq, но реализовать себя реализацию окурок ILocationRepository? Идея заключается в том, что, если насмехаться, это трудно сделать, может быть, вы не должны этого делать?

public class StubLocationRepository : ILocationRepository 
{ 
    private readonly IEnumerable<Location> _findByInclude; 

    public StubLocationRepository(IEnumerable<Location> findByInclude) 
    { 
     _findByInclude = findByInclude; 
    } 

    public IEnumerable<Location> FindByInclude(
     Expression<Func<Location, bool>> predicate, 
     params Expression<Func<Location, object>>[] includeProperties) 
    { 
     return _findByInclude; 
    } 
} 

Это упрощенно, поскольку предполагает, что у вас есть только один метод. Если у вас есть много и вы не хотите передавать постоянные значения для каждого из них, вы можете иметь ctor заглушки, чтобы получить дополнительные параметры, чтобы вы только заглушили нужные методы.

Кроме того, поскольку ILocationRepository, скорее всего, наследуется от общего интерфейса, у вас может быть общая реализация заглушки, которую вы создаете подкласс для создания определенных заглушек - i.e для реализации методов, которые определяет ILocationRepository.

+0

Отличное предложение - большое спасибо - я буду помнить об этом для будущих времен, когда я застрял, отметив правильный ответ Nkosi, поскольку он был быстрее реализован - у меня был только некоторый синтаксис ошибки. Cheers – user1750537

+1

Мне тоже нравится эта идея и согласна с мантрой трудности. – Nkosi

+0

@Nkosi Спасибо, исправлено. –

Смежные вопросы