Почему мой тест Scalacheck с пользовательским генератором не выполняется после отмены многих случаев, и как я могу это исправить?

Я новичок в Scala, и я пишу свой первый набор Scalacheck.

У меня есть структура данных в моей программе, которая по сути выглядит как (List[Double], List[Double]) который хорошо сформирован, только если каждый элемент _1 строго больше, чем соответствующий элемент _2,

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

Затем я добавил два тривиальных теста (включая самый тривиальный из всех, 1 == 1) и в обоих случаях тесты не пройдены, с сообщениемGave up after only XX passed tests. YYY tests were discarded.

Почему это и как мне это исправить?

Прилагается мой набор тестов и вывод.


package com.foo.bar

import org.scalacheck._
import Prop._
import Arbitrary._

object FooSpecification extends Properties("FooIntervals") {

  type FooIntervals = (List[Double], List[Double])

  /* This is supposed to be a tuple of lists s.t. each element of _1
   *  is < the corresponding element of _2
   * 
   *  e.g. (List(1,3,5), List(2,4,6))
   */

  implicit def arbInterval : Arbitrary[FooIntervals] =
    Arbitrary {
      /**
        * Yields a pair (low, high) s.t. low < high
        */
      def GenPair : Gen[(Double, Double)] = for {
        low <- arbitrary[Double]
        high <- arbitrary[Double].suchThat(_ > low)
      } yield (low, high)

      /**
        * Yields (List(x_1,...,x_n), List(y_1,...,y_n))
        * where x_i < y_i forall i and 1 <= n < 20
        */
      for {
        n <- Gen.choose(1,20)
        pairs : List[(Double, Double)] <- Gen.containerOfN[List, (Double, Double)](n, GenPair)
      } yield ((pairs.unzip._1, pairs.unzip._2))
    }

  property("1 == 1") = forAll {
    (b1: FooIntervals)
    =>
    1 == 1
  }

  property("_1.head < _2.head") = forAll {
    (b1: FooIntervals)
    =>
    b1._1.head < b1._2.head
  }
}

[info] ! FooIntervals.1 == 1: Gave up after only 32 passed tests. 501 tests were discarded.
[info] ! FooIntervals._1.head < _2.head: Gave up after only 28 passed tests. 501 tests were discarded.
[info] ScalaTest
[info] Run completed in 1 second, 519 milliseconds.
[info] Total number of tests run: 0
[info] Suites: completed 0, aborted 0
[info] Tests: succeeded 0, failed 0, canceled 0, ignored 0, pending 0
[info] No tests were executed.
[error] Failed: Total 1, Failed 1, Errors 0, Passed 0
[error] Failed tests:
[error]     com.foo.bar.FooSpecification

1 ответ

Решение

arbitrary[Double].suchThat(_ > low) Это твоя проблема. suchThat отбросит все случаи, когда условие ложно. Вы берете два случайных значения и отбрасываете все случаи, когда одно из этих значений больше другого, что будет много. Ты можешь использовать retryUntil вместо suchThat который будет генерировать новые значения до тех пор, пока условие не будет выполнено, а не отбрасывать значения, но это имеет обратную сторону: он может занять много времени или даже зацикливаться вечно, если условие очень маловероятно (представьте, что вы получили очень высокое значение для lowВы могли бы зацикливаться в течение долгого времени, чтобы получить максимум, превышающий его, или навсегда, если вам не повезло иметь максимально возможное двойное значение как низкое.

Что бы работать Gen.choose(low, Double.MaxValue) который выберет значение между low а также Double.MaxValue (максимально возможный двойной).

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

Другие вопросы по тегам