-
Notifications
You must be signed in to change notification settings - Fork 41
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
Discuss adding labels for fuzzed values #94
Comments
I'd like to revive this discussion, as I'd be happy to work on this and we're closing in on releasing 2.0.0. Sorry for pings in case those are unwanted - I'm basing them on a) who participated in the Discourse discussion, and b) who I usually use as my sounding board with testing/fuzzing ideas. In no particular order, @BrianHicks @gampleman @harrysarson @drathier @ianmackenzie @rtfeldman @jonathanjouty There are IMHO a few separate ideas, I'll try to summarize (paraphrasing @mgold's summary on Discourse):
First of all, is there something I'm missing? The Discourse post is long, the John Hughes' talk has many ideas, there are various implementations in various PBT libraries... Other than that, would you folks be OK with me working on these? Currently I favor something like myFuzzer : Fuzzer Int
myFuzzer =
Fuzz.intRange 1 20
justReportFuzzer : Fuzzer Int
justReportFuzzer =
myFuzzer
|> Fuzz.reportCoverage
[ ( "Lower boundary (1)", \n -> n == 1 )
, ( "Upper boundary (20)", \n -> n == 20 )
, ( "In the middle (2..19)", \n -> n > 1 && n <= 20 )
, ( "Outside boundaries??", \n -> n < 1 || n > 20 )
]
expectCoverageFuzzer : Fuzzer Int
expectCoverageFuzzer =
myFuzzer
|> Fuzz.expectCoverage
[ ( 10, "Lower boundary (1)", \n -> n == 1 )
, ( 10, "Upper boundary (20)", \n -> n == 20 )
, ( 50, "In the middle (2..19)", \n -> n > 1 && n <= 20 )
, ( 0, "Outside boundaries??", \n -> n < 1 || n > 20 )
]
mandateCoverageFuzzer : Fuzzer Int
mandateCoverageFuzzer =
myFuzzer
|> Fuzz.mandateCoverage
[ ( 10, "Lower boundary (1)", \n -> n == 1 )
, ( 10, "Upper boundary (20)", \n -> n == 20 )
, ( 50, "In the middle (2..19)", \n -> n > 1 && n <= 20 )
, ( 0, "Outside boundaries??", \n -> n < 1 || n > 20 )
]
-- Usage in tests doesn't change, there is no Test/Expect API change:
justReport : Test
justReport =
Test.fuzz justReportFuzzer "justReport" <| \n -> Expect.true
expectCoverage : Test
expectCoverage =
Test.fuzz expectCoverageFuzzer "expectCoverage" <| \n -> Expect.true
mandateCoverage : Test
mandateCoverage =
Test.fuzz mandateCoverageFuzzer "mandateCoverage" <| \n -> Expect.true
-- Possible to test the labels out in REPL:
> Fuzz.labelExamples { tries = 100, seed = 1 } mandateCoverageFuzzer
[ ( "Lower boundary (1)", Just 1 )
, ( "Upper boundary (20)", Just 20 )
, ( "In the middle (2..19)", Just 5 )
, ( "Outside boundaries??", Nothing )
] |
A few clarifications on my current intentions with the above:
[ ( "green", isGreen )
, ( "red", isRed )
, ( "big", isBig )
] we'd report all the occurring overlaps:
(eg. red,big == 0x, red,green == 0x, red,green,big == 0x, big == 23x) What I'm not sure about is whether |
I'd like to re-iterate from that discussion that I strongly believe that the classification makes a lot more sense in the Specifically I love for instance hacking on elm-geometry, since the library has a very well developed library of reusable fuzzers that expressing new tests is typically super easy. I think we should try to guide users into that kind of design. |
@gampleman In addition, I've realized during implementation that making classification live in bad : Fuzzer Bool
bad =
Fuzz.intRange 0 10
|> Fuzz.reportCoverage [...]
|> Fuzz.map ((==) 5) can't retain the coverage request from the first step. |
@Janiczek This looks really nice.
|
For posterity (I'm editing the PR description a bunch), I've went with
The current API I'm trying out is type ExpectedCoverage
= Zero -- "this variant is never generated!"
| AtLeast Int -- the bread and butter option that most users will use
| MoreThan Int The
Fair, I can experiment and see how it looks without the totals! |
What's the point of |
@gampleman The |
For @jonathanjouty: Here are the two versions (with and without counts) together: I personally like the version with counts better, I'll leave this detail up for discussion for now. |
Maybe we could add little bar charts? 😍 |
@gampleman What about these? |
Also I figured I never answered this.
So, I can imagine a situation where you're trying to fuzz a specific variant of your data - perhaps a balanced binary tree that isn't full. So your fuzzer hopefully generates a subset of all possible trees (doesn't generate full trees), and you'd want to check that fuzzer. In the particular test that needs trees to not be full, to test something on them (perhaps some kind of Test.fuzzWith
{ runs = 10000
, coverage = Test.expectCoverage [ (Test.zero, "full", Tree.isFull) ]
}
nonFullTreeFuzzer
"insert does XYZ"
<| \tree -> ... Is it too convoluted as an example usecase for |
I'm not sure why you wouldn't just use Why not put the categorisation into The api I would think would just be I think this is sensible, since the main usage would anyway be: myTest = Test.fuzz myfuzzer "custom type" <|
\data ->
case date of
Foo xyzzy ->
foo xyzzy
|> Expect.greaterThan 30
|> Expect.label "foo"
Bar [single] ->
...
|> Expect.label "single bar" etc. Edit: than |
With the I guess you could still do myTest = Test.fuzz myfuzzer "custom type" <|
\data ->
(case date of
Foo xyzzy ->
foo xyzzy
|> Expect.greaterThan 30
|> Expect.label "foo"
Bar [single] ->
...
|> Expect.label "single bar"
)
|> Expect.labelIf isBleh "bleh"
|> Expect.labelIf isBlah "blah" at the end of the test, it just isn't very nice with the need to parenthesize. I'm guessing the
Anything that I'm missing? |
I’m wondering if don’t need to experiment with this feature in practice to get enough perspective to decide this correctly. I’m not confident of a good use case for labelling “independently”, since the purpose is to explore different code paths, but perhaps the conditionals are inside the code you are testing and it’s more convenient to do the categorisation outside. I think another disadvantage of the way it’s setup in Test is that it doesn’t naturally lead to exhaustiveness checking by the compiler, unlike a more standard implementation using a case statement. Furthermore I think that ergonomically I’d like to be able to leave the logic of labelling in the code, but have an easy way to turn on/off reporting. |
There's a good discussion happening on discourse about adding labels and coverage requirements to elm-test. Don't do what I did, watch the 50 minute talk linked to in the first post before trying to say something intelligent.
There may be breaking changes that come out of the discussion to I want to hold open the opportunity to make them.
The text was updated successfully, but these errors were encountered: