Свойство над положительными числами не должно сжиматься до отрицательных чисел - PullRequest
0 голосов
/ 26 ноября 2018

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

Сжатие должно помочь найти минимальный сбойный случай.Сжатие до значений за пределами желаемого диапазона сбивает с толку и бесполезно.Это известная ошибка, см. Проблема ScalaCheck # 129 Gen.suchЭто не учитывается при сжатии

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

Пример

Вот минимальный тест свойства:

class ShrinkProp extends Properties("Shrink") {
  property("posNum[Int]") = {
    Prop.forAll(Gen.posNum[Int]) { _: Int =>
      Prop.falsified
    }
  }
}

Обычно это приводит к тому, что ScalaCheck сокращает аргумент до нуля:

[info] Done compiling.
[info] ! Shrink.posNum[Int]: Falsified after 0 passed tests.
[info] > ARG_0: 0
[info] > ARG_0_ORIGINAL: 1
[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

Или, что еще хуже, иногда он может уменьшиться до отрицательного значения:

[info] ! Shrink.posNum[Int]: Falsified after 5 passed tests.
[info] > ARG_0: -1
[info] > ARG_0_ORIGINAL: 3
[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

Отключить сжатие

Одно из решений - отключить сжатие с помощью forAllNoShrink:

class ShrinkProp extends Properties("Shrink") {
  property("posNum[Int]") = {
    Prop.forAllNoShrink(Gen.posNum[Int]) { _: Int =>
      Prop.falsified
    }
  }
}

Результат не уменьшается:

[info] ! Shrink.posNum[Int]: Falsified after 0 passed tests.
[info] > ARG_0: 1
[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

Добавление ограждений

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

import Prop.BooleanOperators

class ShrinkProp extends Properties("Shrink") {
  property("posNum[Int]") = {
    Prop.forAll(Gen.posNum[Int]) { x: Int =>
      (x >=  1) ==> Prop.falsified
    }
  }
}

Есть ли альтернатива отключению сжатия и добавлению охранников?

1 Ответ

0 голосов
/ 26 ноября 2018

В ScalaCheck нет сокращения для положительных целых чисел.Вы должны написать свой собственный.

Обзор

A Shrink должен быть определен как implicit в области проверки вашего свойства.Затем Prop.forAll найдет правильный класс Shrink, если он находится в области видимости и имеет соответствующую сигнатуру типа для значения, которое не прошло тест.

По сути, экземпляр Shrink является функцией, которая преобразуетошибочное значение x для потока «сжатых» значений.Его типовая сигнатура примерно равна:

trait Shrink[T] {
  def shrink(x: T): Stream[T]
}

Вы можете определить Shrink с помощью метода apply объекта-компаньона, который примерно такой:

object Shrink {
  def apply[T](s: T => Stream[T]): Shrink[T] = {
    new Shrink[T] {
      def shrink(x: T): Stream[T] = s(x)
    }
  }
}

Ответ: Сокращение натуральных чисел

Сжатие для положительных целых чисел - это Stream, которое уменьшается вдвое, чтобы найти наименьший сбойный случай при бинарном поиске, но останавливается до достижения нуля:

class ShrinkProp extends Properties("Shrink") {

  implicit val posIntShrinker: Shrink[Int] = Shrink { x: Int =>
    Stream.iterate(x / 2) { x: Int =>
      x / 2
    }.takeWhile { x: Int =>
      x > 0 // Avoid zero.
    }
  }

  property("posNum[Int]") = {
    Prop.forAll(Gen.posNum[Int]) { _: Int =>
      Prop.falsified
    }
  }
}

Доказательство этого сбояработает:

[info] ! Shrink.posNum[Int]: Falsified after 6 passed tests.
[info] > ARG_0: 2
[info] > ARG_0_ORIGINAL: 4
[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

Еще лучше, вы могли бы написать свойство, чтобы проверить, как ваш усадчик ведет себя как следует:

property("posIntShrinker") = {
  Prop.forAll { x: Int =>
    val shrunk = Shrink.shrink(x)
    Prop.atLeastOne(
      (x >= 2) ==> shrunk.size > 0,
      (x <= 1) ==> shrunk.isEmpty
    )
  }
}
[info] + Shrink.posIntShrinker: OK, passed 100 tests.
[info] Failed: Total 1, Failed 0, Errors 0, Passed 1

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

...