Как переопределить некоторые генераторы для ScalaCheck, чтобы принудительно (автоматически) генерировать уточненные типы? Например, только непустые списки

#scala #scalacheck #property-based-testing

#scala #scalacheck #тестирование на основе свойств

Вопрос:

У меня довольно большая структура классов case, и где-то глубоко внутри этой структуры у меня есть поля, которые я хочу уточнить, например, сделать списки непустыми. Можно ли указать ScalaCheck, чтобы сделать эти списки непустыми, используя автоматический вывод из проекта scalacheck-magnolia (без указания каждого поля отдельно)? Пример:

 import com.mrdziuban.ScalacheckMagnolia.deriveArbitrary
import org.scalacheck.Arbitrary
import org.scalacheck.Gen

case class A(b: B, c: C)
case class B(list: List[Long])
case class C(list: List[Long])

// I've tried:
def genNEL[T: Gen]: Gen[List[T]] = Gen.nonEmptyListOf(implicitly[Gen[T]])
implicit val deriveNEL = Arbitrary(genNEL)

implicit val deriveA = implicitly[Arbitrary[A]](deriveArbitrary)
  

Но это не сработало.

Ответ №1:

Я не уверен, как быть универсальным, поскольку я не знаком с получением автоматического вывода для Arbitrary с помощью scalacheck-magnolia. Похоже, scalacheck-magnolia хорош для получения Arbitrary для классов case, но, возможно, не для контейнеров (списков, векторов, массивов и т.д.).

Если вы хотите просто использовать обычный ScalaCheck, вы могли бы просто определить неявный параметр Arbitrary для A себя. Выполнение этого вручную — это дополнительный шаблон, но его преимущество в том, что у вас больше контроля, если вы хотите использовать разные генераторы для разных частей вашей структуры данных.

Вот пример, когда Arbitrary список длин по умолчанию непустой, но пуст для B .

 implicit val listOfLong =
  Arbitrary(Gen.nonEmptyListOf(Arbitrary.arbitrary[Long]))


implicit val arbC = Arbitrary {
  Gen.resultOf(C)
}

implicit val arbB = Arbitrary {
  implicit val listOfLong =
    Arbitrary(Gen.listOf(Arbitrary.arbitrary[Long]))
  Gen.resultOf(B)
}

implicit val arbA = Arbitrary {
  Gen.resultOf(A)
}

property("arbitrary[A]") = {
  Prop.forAll { a: A =>
    a.b.list.size >= 0 amp;amp; a.c.list.size > 0
  }
}