Как заставить Произвольный ScalaCheck всегда генерировать некоторые особые значения? - PullRequest
8 голосов
/ 27 сентября 2011

Я бы хотел, чтобы все мои свойства всегда тестировались как минимум с фиксированным набором специальных значений в дополнение к некоторым случайным значениям.Я хотел бы определить это в моей спецификации генератора, а не в каждом тесте, использующем этот тип генератора.Например, если бы я генерировал Ints, я бы хотел, чтобы мой генератор всегда генерировал по крайней мере 0, 1 и -1 для каждого теста.Возможно ли это?

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

Ответы [ 2 ]

17 голосов
/ 28 сентября 2011

Прежде всего, уже есть смещение в Scalacheck, так что 0, 1, -1, Int.MaxValue и Int.MinValue с большой вероятностью будут выбраны в дополнение к другим Int значениям,Так что, если это ваше беспокойство, не беспокойтесь об этом.Аналогично, могут генерироваться пустые строки.

Но, если вы хотите воспроизвести это поведение для чего-то другого, используйте Gen.oneOf или Gen.frequency, возможно, в сочетании с Gen.choose.Поскольку oneOf и frequency принимают Gen в качестве параметра, вы можете комбинировать особые случаи с универсальными генераторами.

Например:

val myArb: Arbitrary[Int] = Arbitrary(Gen.frequency(
    1 -> -1, 
    1 ->  0, 
    1 -> 1, 
    3 -> Arbitrary.arbInt.arbitrary
))

В значительной степени выполняет то, что вы просили,с вероятностью 50% произвольных целочисленных значений (что будет связано со смещением, о котором я говорил), и 16,6% для каждого из -1, 0 и 1.

1 голос
/ 30 ноября 2018

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

import org.scalacheck.Gen.{alphaChar, const}
import org.scalacheck.Prop.{forAll, passed}
import org.scalacheck.{Gen, Prop}

// evaluate fn first with some initial values, then with some generated ones
def forAllAfter[A](init: A*)(subsequent: Gen[A])(fn: A => Prop): Prop =
  init.foldLeft(passed) { case (p, i) => p && forAll(const(i))(fn) } && forAll(subsequent)(fn) 

// example of usage
val prop = forAllAfter('a', 'b', 'c')(alphaChar) { c =>
  println(c) 
  passed
}

Функция forAllAfter здесь сначала создает Prop s, используя Gen.const для каждого значения, которое должно быть проверено, затем объединяет их с реквизитами, созданными с использованием генератора последующих значений для тестирования.

Если вы используете ScalaTest, вам нужно смешать черту Checkers в вашем тесте, чтобы оценить результирующий Prop, например, так:

import org.scalatest.WordSpec
import org.scalatest.prop.Checkers
import org.scalacheck.Gen.{alphaChar, const}
import org.scalacheck.Prop.{forAll, passed}
import org.scalacheck.{Gen, Prop}

class TestExample extends WordSpec with Checkers {

  def forAllAfter[A](init: A*)(subsequent: Gen[A])(fn: A => Prop): Prop =
    init.foldLeft(passed) { case (p, i) => p && forAll(const(i))(fn) } && forAll(subsequent)(fn)

  val prop: Prop = forAllAfter('a', 'b', 'c')(alphaChar) { c =>
    println(c)
    passed
  }

  "Test example" should {
    "Work correctly" in {
      check(prop)
    }
  }
}
...