Scala, Specs2, Mockito и null возвращаемые значения - PullRequest
0 голосов
/ 02 сентября 2011

Я пытаюсь протестировать код Scala, используя Specs2 и Mockito. Я относительно новичок во всех трех, и у меня возникли трудности с проверенными методами, возвращающими ноль.

В следующем (расшифровано с некоторыми изменениями имени)

  "My Component's process(File)" should  {

    "pass file to Parser" in new modules {
      val file = mock[File]
      myComponent.process(file)

      there was one(mockParser).parse(file)
    }

    "pass parse result to Translator" in new modules {
      val file = mock[File]
      val myType1 = mock[MyType1]

      mockParser.parse(file) returns (Some(myType1))
      myComponent.process(file)

      there was one(mockTranslator).translate(myType1)
    }

  }

«Передача файла в Parser» работает до тех пор, пока я не добавлю вызов транслятора в SUT, а затем умрет, потому что метод mockParser.parse вернул ноль, что код транслятора не может принять.

Аналогично, «результат анализа передачи в Translator» проходит до тех пор, пока я не попытаюсь использовать результат перевода в SUT.

Реальный код для обоих этих методов никогда не может вернуть ноль, но я не знаю, как сказать Mockito, чтобы ожидания возвращали полезные результаты.

Конечно, я могу обойти это, поставив нулевые проверки в SUT, но я бы предпочел этого не делать, поскольку я стараюсь никогда не возвращать нули и вместо этого использую Option, None и Some.

Было бы замечательно найти указатели на хороший учебник по Scala / Specs2 / Mockito, а также простой пример того, как изменить строку, например

there was one(mockParser).parse(file)

чтобы он возвращал что-то, что позволяет продолжить выполнение в SUT, когда он не имеет дело с нулями.

Пытаясь понять это, я попытался изменить эту строку на

there was one(mockParser).parse(file) returns myResult

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

Если это имеет значение, я использую Scala 2.9.0.

Ответы [ 3 ]

3 голосов
/ 03 сентября 2011

Если вы этого не видели, вы можете посмотреть страницу ожидания макета документации по specs2.

В вашем коде заглушка должна быть mockParser.parse(file) returns myResult

Отредактировано после редактирования Дона:

Произошло недоразумение. То, как вы делаете это во втором примере, является хорошим, и вы должны сделать то же самое в первом тесте:

val file = mock[File]
val myType1 = mock[MyType1]

mockParser.parse(file) returns (Some(myType1))
myComponent.process(file)
there was one(mockParser).parse(file)

Идея модульного тестирования с помощью mock всегда одна и та же: объясните, как ваши mocks работают (заглушки), выполняйте, проверяйте.

Это должно ответить на вопрос, теперь персональный совет:

Большую часть времени, кроме случаев, когда вы хотите проверить некоторые алгоритмические действия (остановка при первом успехе, обработка списка в обратном порядке), вы не должны проверять ожидание в своих модульных тестах.

В вашем примере метод process должен «переводить вещи», поэтому ваши юнит-тесты должны сосредоточиться на нем: высмеивать ваши синтаксические анализаторы и переводчики, ставить их в тупик и проверять только результат всего процесса. Это менее мелкое зерно, но цель юнит-теста не в том, чтобы проверить каждый шаг метода. Если вы хотите изменить реализацию, вам не нужно изменять кучу модульных тестов, которые проверяют каждую строку метода.

1 голос
/ 03 сентября 2011

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

Мне нужно было предоставить разумноевозвращаемое значение по умолчанию для макета в форме org.mockito.stubbing.Answer<T> с типом возврата T.

Я смог сделать это с помощью следующей настройки макета:

val defaultParseResult = new Answer[Option[MyType1]] {
  def answer(p1: InvocationOnMock): Option[MyType1] = None
}
val mockParser = org.mockito.Mockito.mock(implicitly[ClassManifest[Parser]].erasure,
                         defaultParseResult).asInstanceOf[Parser]

посленемного поиска источника для черты org.specs2.mock.Mockito и вещей, которые он вызывает.

И теперь, вместо возврата null, анализ возвращает None, когда он не задан (в том числе, когда ожидается, как в первомtest), который позволяет пройти тест с использованием этого значения в тестируемом коде.

Я, скорее всего, создам метод поддержки тестирования, скрывающий беспорядок в назначении mockParser, и позволяющий мне делать то же самоедля различных типов возврата, поскольку мне понадобится одна и та же возможность с несколькими типами возврата только в этом наборе тестов.

Я не смог найти поддержку для более короткого способа сделать это в org.specs2.mock.Mockito,новозможно, это вдохновит Эрика добавить такое.Приятно иметь автора в разговоре ...

Редактировать

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

def mock[T, A](implicit m: ClassManifest[T], a: org.mockito.stubbing.Answer[A]): T = org.mockito.Mockito.mock(implicitly[ClassManifest[T]].erasure, a).asInstanceOf[T]

определено в org.specs2.mock.MockitoMocker, что на самом деле послужило вдохновением для моего решения выше.Но я не могу понять звонок.mock довольно перегружен, и все мои попытки, похоже, заканчиваются вызовом другой версии и не нравятся мои параметры.

Так что, похоже, Эрик уже включил поддержку для этого, ноЯ не понимаю, как к нему добраться.

Обновление

Я определил черту, содержащую следующее:

  def mock[T, A](implicit m: ClassManifest[T], default: A): T = {
    org.mockito.Mockito.mock(
      implicitly[ClassManifest[T]].erasure,
      new Answer[A] {
      def answer(p1: InvocationOnMock): A = default
    }).asInstanceOf[T]
  }

и сейчасиспользуя эту черту, я могу настроить мой макет как

implicit val defaultParseResult = None
val mockParser = mock[Parser,Option[MyType1]]

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

Мне все еще было бы интересно узнать, как решить эту проблему без добавления этой черты.

0 голосов
/ 03 сентября 2011

Без полного сложно сказать, но можете ли вы проверить, что метод, который вы пытаетесь смоделировать, не является окончательным? Потому что в этом случае Mockito не сможет его смутить и вернет ноль.

Еще один совет, когда что-то не работает, - переписать код с помощью Mockito в стандартном тесте JUnit. Затем, в случае неудачи, на ваш вопрос лучше всего ответит кто-то из списка рассылки Mockito.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...