-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Observing Test Case Distribution #25
Comments
I have never used this kind of thing myself, but I can see the appeal, I usually test generators using |
I think this would be a great addition to Hedgehog for the obvious reasons mentioned by @moodmosaic. I miss them (coming from FsCheck) and would certainly use them if there were present. A related suggestion is to enable tests to fail if the number of tests in certain classifications are above or below a given percentage, or absolute number, or both (and perhaps even to generate more test cases until a sufficient number is met). As the feature is implemented in FsCheck, you have to manually check the output of each test to make sure the distribution is OK. |
I would also really love this feature 👍. I've been looking at the at the code (and the haskell package https://github.com/qfpl/tasty-hedgehog-coverage), but as far as I understood what they do is wrap the whole |
I haven't looked closely at https://github.com/qfpl/tasty-hedgehog-coverag and what this means for F# Hedgehog, but I've ported some of the original paper and IIRC this kind of thing was fairly easy to write. |
This is an interesting talk that touches on several issues relevant to this issue (labeling, coverage testing, etc.) |
I have come to the realization that this is a fairly important feature to me. Without it, for anything but trivial generators, I often feel like I'm flying a bit blind. After having thought a bit about it and re-watched the talk I linked to above, I think the following would be the most useful features:
Unfortunately I don't really have the capacity to really delve into new code-bases at the moment. If the changes turn out to be fairly simple, I may be able to help out if given some help and pointers about where to change what. But given that the "run more tests until statistics are good enough" part of point 1 requires reading and understanding a statistics research paper, I'm not sure it is simple. Though @moodmosaic said above that "this sort of thing" (whatever it was) was fairly simple to write, so here's hoping. In any case, I wanted to share my thoughts. |
Essentially what we want is to add coverage combinators: Notes In the Haskell version:
In the early 2016 .NET/F# version: Lines 453 to 511 in this gist, ported from the original QuickCheck (v1) paper, show a rough/naive implementation of those. |
Also, not sure what you mean by "combinators". Syntax-wise, this could also take the form of custom keywords for the property {
let! myInt = Gen.int32 (Range.exponentialBounded())
classify "zero" (myInt = 0)
} Though if the alternative is something like property {
let! myInt = Gen.int32 (Range.exponentialBounded())
do! Gen.classify "zero" (myInt = 0)
} then I don't really care much either way. |
Agreed 👍 That's what I mean; custom CE keywords are better. Those keywords may use the underlying combinators (to be added) in the Property module. |
This is such a nerd snipe for me. All your links to external references and explanation of the Haskell implementation will be very helpful. Now the only question is when I will get to this. |
@cmeeren, checkCoverage :: Property -> Property
checkCoverage =
verifiedTermination . withConfidence (10^9) I'll provide an example of QuickCheck's |
Great, I would appreciated that! |
@cmeeren, a good example of QuickCheck's I ported all properties from that answer in Hedgehog and created a custom test-runner that'll run sequentially both the original QuickCheck properties and the Hedgehog ones. https://github.com/moodmosaic/coverage-check-example (Install GHCUp, clone the above repo, @TysonMN, if this is ported in F# Hedgehog, perhaps we can have the same API as in Haskell Hedgehog but if possible we'd rather take the statistical part from QuickCheck instead, if my comment in Haskell Hedgehog isn't resolved soon. |
Oh, I see, you were referring to Haskell Hedgehog, not F#. I thought you were referring to F# and that this has somehow been implemented under the radar. In any case, thanks for the clarification. 🙂 |
I'm beginning to realise that after I got clued into this capability I use it more and more. It enables me to write simpler properties. If I may, here's a simpler example than the one linked above. Provoked by Robert C. Martin I was recently doing the Gossiping Bus Drivers kata in Haskell, and I was writing a simple property to verify the image of my System Under Test (SUT), which is this function: drive :: (Num b, Enum b, Ord a) => [[a]] -> Maybe b As you can see, the output is a The simplest way I can think of to express this property is this: testProperty "drive image" $ \ (routes :: [NonEmptyList Int]) ->
let actual = drive $ fmap getNonEmpty routes
in checkCoverage $
cover 75 (isJust actual) "solution exists" $
all (\i -> 0 <= i && i <= 480) actual Why use What's the easiest way to pass that test, if, for example, you'd employ the Devil's Advocate? Just return So I wanted to ensure that the Devil can't do that. How do I do that? Without I find that Why 75%? That particular number was just a result of a bit of trial and error. I didn't really care about the particular percentage, just that it was comfortably greater than zero, so that I knew that there would be multiple test cases that cover the For this particular test, it makes QuickCheck generate 200 tests in order to pass the 75% requirement. |
To start off with a great reference, you are preferring predicative over constructive data.
I find it confusing to use I think a more direct approach to solve this problem would be to use fsharp-hedgehog/src/Hedgehog/Gen.fs Lines 290 to 291 in 253634b
F# Hedgehog continues generating test cases until the desired quantity (100 by default have passed... fsharp-hedgehog/src/Hedgehog/Property.fs Lines 193 to 196 in 253634b
...one has failed... fsharp-hedgehog/src/Hedgehog/Property.fs Lines 210 to 213 in 253634b
...or 100 generated values have been skipped due to predicates passed to fsharp-hedgehog/src/Hedgehog/Property.fs Lines 197 to 200 in 253634b
So I think the simplest change is for us to add the ability to set the |
@TysonMN, thank you for challenging my assumptions. As everyone else, I'm vulnerable to the Golden Hammer syndrome. I've always thought of property filters as something one puts in the beginning of the test, as a sort of preamble. Now that you suggest it, it turns out that there's no reason you can't use it in the assertion step: testProperty "drive image" $ \ (routes :: [NonEmptyList Int]) ->
let actual = drive $ fmap getNonEmpty routes
in isJust actual ==>
all (\i -> 0 <= i && i <= 480) actual This is, indeed, simpler! Cool! Thank you. |
Taken from QuickCheck manual:
Thus, we could consider adding
label
,classify
, andcollect
, in theProperty
module.The text was updated successfully, but these errors were encountered: