Как заглушить IQueryable‹T›.Where(Func‹T, bool›) с помощью Rhino Mocks?

В проекте .net 3.5, над которым я сейчас работаю, я писал несколько тестов для класса обслуживания.

public class ServiceClass : IServiceClass
{
     private readonly IRepository _repository;

     public ServiceClass(IRepository repository)
     {
          _repository = repository;
     }

     #region IServiceClass Members

     public IEnumerable<ActionType> GetAvailableActions()
     {
         IQueryable<ActionType> actionTypeQuery = _repository.Query<ActionType>();
         return actionTypeQuery.Where(x => x.Name == "debug").AsEnumerable();
     }

     #endregion
}

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

actionTypeQuery.Where(x => x.Name == "debug")

часть.

Вот что я получил до сих пор:

[TestFixture]
public class ServiceClassTester
{
    private ServiceClass _service;
    private IRepository _repository;
    private IQueryable<ActionType> _actionQuery;
    [SetUp]
    public void SetUp()
    {
        _repository = MockRepository.GenerateMock<IRepository>();
        _service = new ServiceClass(_repository);
    }

    [Test]
    public void heres_a_test()
    {
        _actionQuery = MockRepository.GenerateStub<IQueryable<ActionType>>();

        _repository.Expect(x => x.Query<ActionType>()).Return(_actionQuery);
        _actionQuery.Expect(x => x.Where(y => y.Name == "debug")).Return(_actionQuery);

        _service.GetAvailableActions();

        _repository.VerifyAllExpectations();
        _actionQuery.VerifyAllExpectations();
    }

}

[Примечание: имена классов были изменены, чтобы защитить невиновных]

Но это не удается с System.NullReferenceException в

_actionQuery.Expect(x => x.Where(y => y.Name == "debug")).Return(_actionQuery);

Итак, мой вопрос:

Как смоделировать или заглушить функцию IQueryable.Where с помощью RhinoMocks и пройти этот тест?

Если моя текущая настройка не позволяет мне имитировать или заглушать IQueryable, дайте аргументированное объяснение, почему.

Спасибо, что прочитали этот эпически длинный вопрос.


person Mark Rogers    schedule 26.04.2009    source источник
comment
Оба верхних ответа были достаточно высокого качества, чтобы их можно было выбрать. Спасибо, извините, если я не выбрал ваш.   -  person Mark Rogers    schedule 27.04.2009


Ответы (4)


Сначала я хотел имитировать такие вызовы, как «IQueryable.Where(Func)», но я думаю, что это тестирование на неправильном уровне. Вместо этого в своих тестах я просто издевался над IQueryable, а затем проверял результат:

// Setup a dummy list that will be filtered, queried, etc
var actionList = new List<ActionType>()
{
    new ActionType() { Name = "debug" },
    new ActionType() { Name = "other" }
};
_repository.Expect(x => x.Query<ActionType>()).Return(actionList);

var result = _service.GetAvailableActions().ToList();

// Check the logic of GetAvailableActions returns the correct subset 
// of actionList, etc:
Assert.That(result.Length, Is.EqualTo(1));
Assert.That(result[0], Is.EqualTo(actionList[0]);

_repository.VerifyAllExpectations();
person Luke Quinane    schedule 27.04.2009
comment
Я хотел сказать то же самое с очень похожим примером. Я не буду совершать. Это очень правильно. Как показывает Люк, вам не нужно издеваться над Where(). Это часть вашей логики. Вы проверяете, правильно ли работает лямбда, переданная в Where(). Просто убедитесь, что ваш actionList.Query возвращает данные, которые можно отфильтровать с помощью Where(), и убедитесь, что запрос работает правильно на основе возвращаемого набора данных. Хорошая работа, Люк. - person Brian Genisio; 27.04.2009

Без использования моков Rhino вы можете создать список, а затем вызвать для него .AsQueryable(). например

var actionTypeList = new List<ActionType>() {
    new ActionType {},  //put your fake data here
    new ActionType {}
};

var actionTypeRepo = actionTypeList.AsQueryable();

Это, по крайней мере, даст вам поддельный репозиторий, но не позволит вам проверить, были ли вызваны методы.

person Lance Fisher    schedule 26.04.2009

Where — это метод расширения, это не метод, реализованный интерфейсом IQueriable. Посмотрите на членов IQueriable: http://msdn.microsoft.com/en-us/library/system.linq.queryable_members.aspx

Метод расширения является статическим и не может быть смоделирован. ИМО, нет необходимости издеваться над Where, потому что это часть языка. Вы должны только издеваться над репозиторием.

Изменить, пример:

[TestFixture]
public class ServiceClassTester
{
    private ServiceClass _service;
    private IRepository _repository;
    private IQueryable<ActionType> _actionQuery;

    [SetUp]
    public void SetUp()
    {
        _service = new ServiceClass(_repository);

        // set up the actions. There is probably a more elegant way than this.
        _actionQuery = (new List<ActionType>() { ActionA, ActionB }).AsQueryable();

        // setup the repository
        _repository = MockRepository.GenerateMock<IRepository>();
        _repository.Stub(x => x.Query<ActionType>()).Return(_actionQuery);
    }

    [Test]
    public void heres_a_test()
    {
        // act
        var actions = _service.GetAvailableActions();

        // assert
        Assert.AreEqual(1, actions.Count());
        // more asserts on he result of the tested method
    }

}

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

person Stefan Steinegger    schedule 27.04.2009

У меня была аналогичная проблема, когда я пытался заглушить выражение предиката для метода Find, который содержит строку, но String является ссылочным типом и, конечно, неизменяем. Никакого успеха, пока я не создам testPredicate, который я передам заглушке, фактическому SUT и, наконец, утвердить. Ниже код работает.

    [Test()]
    public void Search_Apartment_With_Spesific_Address()
    {
        //ARRANGE
        var repositoryMock = MockRepository.GenerateMock<IApartmentRepository>();
        var notificationMock = MockRepository.GenerateMock<INotificationService>();
        var service = new ApartmentService(repositoryMock, notificationMock);
        var apartment = new List<Apartment> {new Apartment {Address = "Elm Street 2"}}.AsQueryable();

        Expression<Func<Apartment, bool>> testPredicate = a => a.Address == "Elm Street 2";
        repositoryMock.Stub(x => x.Find(testPredicate)).Return(apartment);

        //ACT
        service.Find(testPredicate);

        //ASSERT
        repositoryMock.AssertWasCalled(x => x.Find(testPredicate));
    }
person Pasi Heinonen    schedule 15.11.2012