Reputation: 674
I am using scalacheck and would like to evaluate the same set of properties on two functions f1
and f2
, but I would like to interpret the outcomes differently. For f1
I would like the properties to pass, while for f2
I would like the properties to fail. This is needed for simple mutation testing (to check whether the test is good enough to detect bugs).
I have put my tests in a class, which I parameterize with the function to test. I made the class abstract to prevent the test runner from running this class (otherwise it fails with an error that there is no nullary constructor).
abstract class MySpec(f: SOMETYPE, suffix: String)
extends org.scalacheck.Properties(s"MySpec.${suffix}"):
property("trivial") = true
Now in a test file I would like to instantiate it twice. Something like this:
object CorrectSpec extends MySpec(f1, "correct")
abstract class MutantSpec extends MySpec(f2, "mutant")
The second spec is abstract to prevent it from executing before I negate the properties.
Unfortunately, I could not figure out how to do this, at least not without a major change to how MySpec
is written, which I would like to avoid - this is code facing my users, and I would like to hide what I am doing behind the scenes.
I tried various things, but they all end up in one of two corners. I can extract properties using MySpec(f2, "mutant").properties
and then map
or flatMap
to create the negated ones. For example with flatMap (just for the first property, for simplicity):
mutantSpec.properties(0).flatMap { result => result.status == False }
Now that I have the negated properties, I need to add them to some spec objects for execution.
(A) I can inject them in MutantSpec
(extending MutantSpec
and using property.update
). Sadly, I am getting an error that updating properties during execution is not allowed. This is a strange message, as this is not more "during execution" than the other properties defined in the same file. This seems to be caused by the properties
methods that I use to access the existing "correct" properties. It seems to set a flag that the values have leaked (so further updates are not allowed).
(B) I can also inject the properties into a new spec that does not extend MutantSpec
. Then the error does not appear (as I am modifing another spec object than the one "frozen" by accessing properties
. But in order to call properties
from outside MutantSpec
I have to instantiate MutantSpec
which makes scala-cli
execute the unmodfied MutantSpec
and by-design this spec will fail (as it is the non-negated spec, running on a buggy f2
). This pollutes my test results and breaks CI.
Any idea? Did I get into a corner scalacheck's designers have not anticipated? The second idea (B) seems closer as it runs, just slightly too many tests. Perhaps I can filter out the annoying tests with a command line option, but as far as I can see only a positive filter (-f
) is available. I worked around this by formatting my messages so all the actual tests contain a selected string, and those that are spurious do not. It is not ideal, as it limits the names of tests, but I could not figure out a better way.
Upvotes: 1
Views: 50