использование specs2 с привязкой scalaz-scalacheck-binding для проверки законов

Я нахожу использование specs2 со scalacheck для проверки законов моноидов немного уродливым при попытке использовать библиотеку привязки scalacheck scalaz. В моем коде используется моноид scalaz, поэтому я хотел использовать их законы, чтобы убедиться, что мой MyType их реализует.

Это уродство заставляет меня думать, что я что-то упускаю или неправильно использую Specs2 или scalacheck-binding API. Предложения приветствуются.

Вот что я сделал: -

Я использую specs2 3.7 с scalaz 2.7.0.

Чтение руководства пользователя на "http://etorreborre.github.io/specs2/guide/SPECS2-3.0/org.specs2.guide.UseScalaCheck.html" Я расширил свою спецификацию чертой Scalacheck, и у меня есть область действия Arbitrary[MyType], поэтому я должен иметь возможность использовать Скалапроверить ок.

В упомянутом выше документе говорится, что мне нужно передать функцию методу prop, если переданная функция возвращает Result, где Prop scalacheck является допустимым Result.

API scalacheck-binding дает мне функцию monoid.laws[T], которая возвращает Properties, который является Prop, так что это должно быть в порядке, он также принимает неявные параметры типов Monoid[T], Equal[T] и Arbitrary[T], все из которых у меня есть в области видимости, где T есть MyType

Я хочу сделать это:

class MyTypeSpec extends Specification with ScalaCheck {
  def is = s2"""
   MyType spec must :-
     obey the Monoid Laws $testMonoidLaws
  """

  def testMonoidLaws = {
    import org.scalacheck.{Gen, Arbitrary}
    import scalaz.scalacheck.ScalazProperties._
    implicit val arbMyType: Arbitrary[MyType] = genArbMyTpe() // an helper Arbitrary Gen func i have written
    prop { monoid.laws[MyType] }
  }
}

но prop cannot be applied to (org.scalacheck.Properties) Требуется, чтобы T в Arbitrary был типом параметра функции, поэтому я сделал это, обратите внимание, что я выбрасываю параметр t, ...

class MyTypeSpec extends Specification with ScalaCheck {
  def is = s2"""
   MyType spec must :-
     obey the Monoid Laws $testMonoidLaws
  """

  def testMonoidLaws = {
    import org.scalacheck.{Gen, Arbitrary}
    import scalaz.scalacheck.ScalazProperties._
    implicit val arbMyType: Arbitrary[MyType] = genArbMyTpe() //some Arbitrary Gen func
    prop { (t: Path => monoid.laws[MyType] }
  }
}

Мой тест проходит. ура! Так в чем проблема?

Я беспокоюсь о тесте. Все говорит, что прошло. Я не получаю вывода, как если бы использовал Scalacheck, прямо говорящий мне, какие законы он выполнил и передал. Также я отбрасываю параметр t и позволяю monoid.laws[MyType] найти имплициты в области видимости, что кажется неправильным. Это работает? я исказил API спецификаций2?

модифицируя MyType, чтобы он определенно провалился, законы привели к провалу теста, что хорошо, но я все еще беспокоюсь, так как он всегда терпит неудачу с

Falsified after 0 passed tests.

Я могу собрать Arbitrary[MyType], выполнив

prop { (p: Path) => monoid.laws[Path] }.collectArg(f => "it was " + f.shows)

затем запустить его так

sbt testOnly MyTypeSpec -- scalacheck.verbose

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

Есть ли лучший способ тестирования с использованием Specs2 и scalaz scalacheck-bindings, который менее уродлив и выводит информацию, которая дает мне уверенность в том, что законы были опробованы и протестированы?

Спасибо

Карл


person Karl    schedule 06.01.2016    source источник


Ответы (1)


Вы можете использовать Properties напрямую, не используя prop. Вот полный пример:

import org.specs2._
import scalaz.scalacheck.ScalazProperties._
import org.scalacheck._
import scalaz._, Scalaz._
import PositiveInt._

class TestSpec extends Specification with ScalaCheck { def is = s2"""

 PositiveInt should pass the Monoid laws $e1

"""
  def e1 = monoid.laws[PositiveInt]
}

case class PositiveInt(i: Int)

object PositiveInt {
  implicit def ArbitraryPositiveInt: Arbitrary[PositiveInt] =
    Arbitrary(Gen.choose(0, 100).map(PositiveInt.apply))

  implicit def EqualPositiveInt: Equal[PositiveInt] =
    Equal.equalA[PositiveInt]

  implicit def MonoidPositiveInt: Monoid[PositiveInt] = new Monoid[PositiveInt] {
    val zero = PositiveInt(1)
    def append(p1: PositiveInt, p2: =>PositiveInt): PositiveInt =
      PositiveInt(p1.i + p2.i)
  }
}

И поскольку экземпляр Monoid неверен, он завершится ошибкой:

[info] TestSpec
[info]
[error]  x PositiveInt should pass the Monoid laws
[error]  Falsified after 0 passed tests.
[error]  > Labels of failing property:
[error]  monoid.left identity
[error]  > ARG_0: PositiveInt(3)
[info]
[info]
[info] Total for specification TestSpec
[info] Finished in 185 ms
[info] 1 example, 1 failure, 0 error

Неудача указывает на первые законы, которые не проходят. Однако он не создает несколько примеров, по одному для каждого закона, чтобы показать, какой закон выполняется. Если вы хотите сделать это, вы можете сопоставить каждое свойство законов Properties с примером: класс TestSpec расширяет спецификацию с помощью ScalaCheck { def is = s2"""

 PositiveInt should pass the Monoid laws $properties

"""

  def properties = toExamples(monoid.laws[PositiveInt])

  def toExamples(ps: Properties): Fragments =
    t ^ Fragments.foreach(ps.properties) { case (name, prop) => br ^ name ! prop }
}

Это печатает (для проходящего экземпляра Monoid[PositiveInt]):

[info] TestSpec
[info]
[info]  PositiveInt should pass the Monoid laws
[info]   + monoid.semigroup.associative
[info]   + monoid.left identity
[info]   + monoid.right identity
[info]
[info] Total for specification TestSpec
[info] Finished in 91 ms
[info] 3 examples, 300 expectations, 0 failure, 0 error
person Eric    schedule 07.01.2016
comment
Спасибо, Эрик. Мне было непонятно, что я могу просто вернуть Properties прямо из документации, где во всех примерах используется prop. Спасибо - person Karl; 20.01.2016