0

Given this:

  property("Empty range") {
    forAll { (min: Int, max: Int) =>
      whenever (min == max) {
        Range(min, max).size should be (0)
      }
    }
  }

I get

[info] - Empty range *** FAILED *** 
[info]   Gave up after 5 successful property evaluations. 96 evaluations were discarded.

How do I express my test case, which is to capture the property of a Range that regardless of a and b, if they are equal then Range(a,b) should be empty.

Another one:

  property("10 long range") {
    forAll { (min: Int, max: Int) =>
      whenever (min < max && (max.toLong-min.toLong).abs == 10) {
        Range(min, max).head should be (min)
      }
    }
  }

I have a bunch of test cases like this (for a class similar to Range), all of them failing with the same error.

I would like to capture Ranges with a given size, and testing elements within that range - the idea being that I want ScalaCheck to generate the Range boundaries for me.

egbokul
  • 3,794
  • 4
  • 33
  • 50

1 Answers1

1

Sorry for the tardy reply, but, for your first case, since you're only testing that the range for two matching values is 0, you only need to generate one value:

property("Empty range") {
  forAll { x: Int =>
    Range(x, x).size should be (0)
  }
}

By using a whenever case for this test, the probability of generating two equal values is very low, so you'll waste a lot of combinations of min & max that aren't equal. Eventually, it will give up trying to find combinations that satisfy the condition. The alternative above will work for every generated value, so it's more efficient too.

The second example is similar. First, all the cases in which min is >= max will be discarded. Then, all the cases in which the difference between the two values isn't 10 will be discarded. So, once more, the chances of generating a min value that is exactly 10 less than the max value are so low that the test will give up trying to find values that satisfy this condition. Again, the following is equivalent to what you have and will work for the majority of generated values (only those within 10 of the maximum value will need to be discarded to avoid overflow on the addition).

property("10 long range") {
  forAll { x: Int =>
    whenever (x <= Int.MaxValue - 10) {
      Range(x, x + 10).head should be (min)
    }
  }
}

However, your tests seem to be a little unusual in that you're testing some very specific cases, when you should instead be looking for more general conditions. For example:

property("Range size") {
  forAll { (min: Int, max: Int) =>
    whenever (min <= max && (max.toLong - min.toLong) <= Int.MaxValue) {
      Range(min, max).size should be (max - min)
    }
  }
}

should cover all of the size comparisons, including the case where the two values are equal and the size is 0. (The whenever clause is required in this case to prevent min values being higher than the max values and to ensure that the range size does not exceed the capacity of an Int). You could also write this test, slightly more efficiently in terms of generated numbers, like this:

property("Range size #2") {
  forAll { (a: Int, b: Int) =>
    whenever (Math.abs(a.toLong - b.toLong) <= Int.MaxValue) {
      val min = Math.min(a, b)
      val max = Math.max(a, b)
      Range(min, max).size should be (max - min)
    }
  }
}

In both of the above cases, the whenever clause only occasionally fails - particularly in the latter case - rather than virtually all the time.

Mike Allen
  • 7,554
  • 2
  • 20
  • 45