Rhino выдает ошибку: предыдущий метод ‘IEnumerator.MoveNext();’ требует возвращаемого значения или исключения для выброса

#.net #unit-testing #c#-4.0 #rhino-mocks

#.net #модульное тестирование #c #-4.0 #rhino-mocks

Вопрос:

У меня есть следующий тестовый код:

 parentViewModel = MockRepository.GenerateMock<IParentViewModel>();
parentViewModel.Expect(x => x.GetPropertyValue<IEnumerable<Milestone>>("JobMilestones")).Return(new Milestone[0]);

viewModel = new JobPenaltiesViewModel(j, new Penalty[0], _opContext, parentViewModel);

Assert.That(viewModel.Milestones.Count(), Is.EqualTo(0));
parentViewModel.VerifyAllExpectations();

List<string> propsChanged = new List<string>();
viewModel.PropertyChanged  = (s, e) => propsChanged.Add(e.PropertyName);

parentViewModel.Raise(x => x.PropertyChanged  = null, parentViewModel, new PropertyChangedEventArgs("JobMilestones"));

AssertPropertiesChangedAsExepected(propsChanged, 1, "Milestones");

Milestone m1 = GenerateMilestone(j);
List<Milestone> milestones1 = new List<Milestone> { m1 };
parentViewModel.Expect(x => x.GetPropertyValue<IEnumerable<Milestone>>("JobMilestones")).Return(milestones1).Repeat.Any();

IEnumerable<Milestone> milestones = viewModel.Milestones;
Assert.That(milestones.Count(), Is.EqualTo(1));
parentViewModel.VerifyAllExpectations();
  

Все тесты и утверждения выполняются успешно до тех пор, пока:

 Assert.That(milestones.Count(), Is.EqualTo(1));
  

Вот где я получаю исключение:

 Previous method 'IEnumerator.MoveNext();' requires a return value or an exception to throw.
  

Я перепробовал все, что мог придумать, и мое тестирование, похоже, показывает, что макет parentViewModel возвращает null или пустое перечисление (т. Е. Когда я использую отладчик для проверки возвращаемого значения, «Просмотр результатов» сообщает, что перечисление не вернуло результатов).

Чего мне здесь не хватает?

Комментарии:

1. Получите ли вы правильный результат, если попытаетесь выполнить: IEnumerable<Milestone> milestones = parentViewModel.Milestones; и Assert.That(milestones.Count(), Is.EqualTo(1)) ? (Просто проверьте, где вы потеряли m1)

2. Нет, Assert.That(milestones.Count(), Is.EqualTo(1)) является источником исключения. Я никогда не добираюсь до parentViewModel.VerifyAllExpectations()

3. Хорошо, я не очень понял, что я имел в виду. Я имел в виду добавление строк сразу после, parentViewModel.Expect(x => x.GetPropertyValue<IEnumerable<Milestone>>("JobMilestones")).Return(milestones1).Repeat.Any(); чтобы вы утверждали на parentViewModel перед утверждением на viewModel . Чтобы убедиться, что это m1 находится в parentViewModel.JobMileStones (я написал parentViewModel. Основные этапы в первом комментарии — моя ошибка.).

4. Это дает мне: This action is invalid when the mock object ... is in record state. Я не уверен, почему он жалуется, что я в режиме записи, я использую синтаксис AAA. Нужно ли мне каким-то образом сбросить макет, прежде чем я попытаюсь вернуть второе значение?

5. На самом деле сейчас я удалил этот раздел кода; после дальнейшего просмотра я понял, что он ничего не добавляет к моему тесту. Что я действительно хочу здесь протестировать, так это то, что когда родительский элемент уведомляет, что его коллекция «JobMilestones» изменилась, дочерний элемент уведомляет, что его коллекция «Milestones» изменилась. Тем не менее, я все еще хотел бы разобраться в этой проблеме, поэтому, если кто-нибудь сможет ответить на нее, я с радостью предоставлю ответ.

Ответ №1:

milestones.Count() выполняется подобным образом (поскольку это объект IEnumerable):

  1. Установите счетчик в 0.
  2. Получить первый элемент.
  3. Добавьте 1 к счетчику.
  4. Перейти к следующему элементу.
  5. Шаг 3, пока следующий элемент не станет нулевым
  6. Возвращает счетчик

Поэтому я предлагаю вам немного переписать.

Вариант 1:

  1. Создайте не коллекцию IEnumerable, а какой-нибудь более сильный объект, такой как List или Array :

    var milestones = viewModel.Milestones.ToArray();
    //var milestones = viewModel.Milestones.ToList();

    После этого вы можете использовать соответственно Count и Length свойство для Assert проверки:

    Assert.That(milestones.Count, Is.EqualTo(1));
    //Assert.That(milestones.Length, Is.EqualTo(1));

  2. Создайте локальную переменную для хранения параметра count:

    var count = viewModel.Milestones.Count(); // .Count() method executes here.
    Assert.That(count, Is.EqualTo(1));

Комментарии:

1. Я понимаю, что вы говорите, и в этом есть некоторый смысл. Но вопрос, который у меня все еще есть, заключается в том, что вызов parentViewModel должен возвращать реальный List<> объект (обратите внимание на Expect вызов). Таким образом, вызову Count() даже не нужно перечислять его, он должен вызывать свойство Count в списке. Это почти так, как если бы RhinoMocks переписывал возвращаемое значение.

2. @Coding Gorilla Я должен отметить, что вы вызываете .Count() метод IEnumerable<Milestone> , а не Count свойство List<Milestone> , поэтому это приводит к перечислению IEnumerable<Milestone> .

3. Это неверно, the . Метод Count() достаточно умен, чтобы выяснить, поддерживает ли тип underly свойство Count, и будет использовать это свойство, если оно доступно. Смотрите замечания здесь: msdn.microsoft.com/en-us/library/bb535181.aspx

4. Метод @Coding Gorilla В статье .Count() вызывается для Array объекта. В вашем случае вы вызываете IEnumerable<Milestone>.Count() , потому что вы выполнили приведение из List в Ienumerable . Вы пробовали вызывать подобные методы: viewModel.Milestones.ToArray().Count() ?

5. Базовым типом является a, List<> и Count() метод пытается привести исходный код как ICollection , и если это успешно, он возвращает Count свойство (я декомпилировал метод, чтобы проверить это). Таким образом, на самом деле не должно быть необходимости перечислять коллекцию. Отладчик, однако, показал, что базовый тип является прокси-сервером, сгенерированным (по-видимому) RhinoMocks, я думаю, это та часть, которую я не понимаю.

Ответ №2:

С тех пор я удалил код-нарушитель; однако я так и не понял, почему он вел себя так, как было.