Fuzzer
overhaulExpect.true
andExpect.false
Test.Runner.Failure.format
removalTest.Html.Event
additions- (low-level)
Test.Runner
changes
There are many changes in the Fuzz
module:
- many helpers added to achieve common patterns in an optimized way
Fuzz.andThen
added 🎉Shrink
andFuzz.custom
is gone 🔥 (allshrinkingsimplifying is now automatic!)- behaviour of some fuzzers has changed (eg.
Fuzz.float
), trying out more edge cases and simplifying to nicer values
Here are the details:
- ❌ Removed: module
Shrink
- ❌ Removed:
Fuzz.custom : Generator a -> Shrinker a -> Fuzzer a
There are now no more Shrinker
s! All simplification is automatic. This is thanks to the integrated shrinking approach popularized by the Hypothesis library.
With Fuzz.custom
gone and with Fuzz.andThen
added, it should now be always possible to mirror your Random.Generator
s with Fuzzer
s. Take a look at the Random to Fuzz translation section for examples and more help.
- ✳️ Added:
Fuzz.examples : Int -> Fuzzer a -> List a
This function lets you test out your fuzzers—say, in REPL—during development. It will use a hardcoded Random seed and generate a few example values from the fuzzer:
Fuzz.examples 10 Fuzz.int
--> [1,68,54,37,111,-32,0,-30,7,36] : List Int
Fuzz.examples 5 Fuzz.int
--> [1,68,54,37,111] : List Int
Fuzz.examples 5 (Fuzz.intRange -10 10)
--> [-3,6,-1,-6,3] : List Int
- ✍️ Changed:
-Fuzz.tuple : ( Fuzzer a, Fuzzer b ) -> Fuzzer ( a, b )
+Fuzz.pair : Fuzzer a -> Fuzzer b -> Fuzzer ( a, b )
- ✍️ Changed:
-Fuzz.tuple3 : ( Fuzzer a, Fuzzer b, Fuzzer c ) -> Fuzzer ( a, b, c )
+Fuzz.triple : Fuzzer a -> Fuzzer b -> Fuzzer c -> Fuzzer ( a, b, c )
The pair and triple-creating functions have changed their API, and should now be nicer to use.
- ✳️ Added:
Fuzz.andThen : (a -> Fuzzer b) -> Fuzzer a -> Fuzzer b
- ✳️ Added:
Fuzz.filter : (a -> Bool) -> Fuzzer a -> Fuzzer a
- ✳️ Added:
Fuzz.sequence : List (Fuzzer a) -> Fuzzer (List a)
- ✳️ Added:
Fuzz.traverse : (a -> Fuzzer b) -> List a -> Fuzzer (List b)
- ✳️ Added:
Fuzz.map6
- ✳️ Added:
Fuzz.map7
- ✳️ Added:
Fuzz.map8
The star of this section is undoubtedly Fuzz.andThen
. This function missing is a big part of why people have been reaching for Fuzz.custom
and defining their fuzzers via Random.Generator
s, where Random.andThen
is available.
With Fuzz.andThen
now being available, you have total freedom in how you'll define your fuzzers. (But also, if some of the helpers like Fuzz.listOfLengthBetween
do what you want already, we advise you to use them instead of Fuzz.andThen
, as they've been tuned to work better with the simplifying algorithm.)
Fuzz.filter
allows you to reject/keep values based on a predicate, but note it will always be better to generate good values instead of generating a mixture of good and bad values and filtering the bad ones out. The Fuzz.filter
function will bail out after 15 consecutive rejections!
- ✳️ Added:
Fuzz.oneOfValues : List a -> Fuzzer a
- ✳️ Added:
Fuzz.frequencyValues : List ( Float, a ) -> Fuzzer a
- ✳️ Added:
Fuzz.shuffledList : List a -> Fuzzer (List a)
Helpers for common patterns.
-- BEFORE
Fuzz.oneOf (List.map Fuzz.constant [ 1, 5, 42, 999, 1000 ])
-- AFTER
Fuzz.oneOfValues [ 1, 5, 42, 999, 1000 ]
- ✳️ Added:
Fuzz.asciiChar : Fuzzer Char
- ✳️ Added:
Fuzz.asciiString : Fuzzer String
- ✳️ Added:
Fuzz.asciiStringOfLength : Int -> Fuzzer String
- ✳️ Added:
Fuzz.asciiStringOfLengthBetween : Int -> Int -> Fuzzer String
- ✳️ Added:
Fuzz.stringOfLength : Int -> Fuzzer String
- ✳️ Added:
Fuzz.stringOfLengthBetween : Int -> Int -> Fuzzer String
Various Char
/String
helpers. Note that the Fuzz.char
and Fuzz.string*
functions will happily give you emoji and other various Unicode characters. The new Fuzz.ascii*
functions will only give you printable ASCII characters (meaning, characters in the 32..126
or 0x20..0x7E
range).
- ✳️ Added:
Fuzz.intAtLeast : Int -> Fuzzer Int
- ✳️ Added:
Fuzz.intAtMost : Int -> Fuzzer Int
- ✳️ Added:
Fuzz.uniformInt : Int -> Fuzzer Int
Integer fuzzers prefer generating smaller values by default. We've introduced functions Fuzz.intAtLeast
and Fuzz.intAtMost
which use Random.minInt
and Random.maxInt
as their limit values, removing the need for you to import the Random
module.
The Fuzz.uniformInt n
fuzzer generates integers in range 0..n
inclusive, without any bias towards smaller values.
Fuzz.examples 10 (Fuzz.intRange 0 2000)
--> [2,136,108,74,222,65,0,61,14,72] : List Int
Fuzz.examples 10 (Fuzz.uniformInt 2000)
--> [1414,491,962,1824,1068,255,1838,1655,1403,847]
- ✳️ Added:
Fuzz.floatAtLeast : Float -> Fuzzer Float
- ✳️ Added:
Fuzz.floatAtMost : Float -> Fuzzer Float
- ✳️ Added:
Fuzz.niceFloat : Fuzzer Float
Floats have received a lot of care in this release. All float fuzzers will now prefer simplifying to nice values:
- integers over fractions
- positive over negative
- simpler fractions over complex fractions
Meaning, you can expect a lot more of 1.5
and a lot less of 1.0000000000014
🎉
Unfortunately there was no easy way to keep this behaviour for Fuzz.floatRange
and certain intervals inside Fuzz.floatAtLeast
and Fuzz.floatAtMost
, so it's preferred to keep using Fuzz.float
if you don't need to constrain the float range.
Fuzzers will also try edge cases like NaN
, Infinity
and -Infinity
. If you don't want these generated, there's Fuzz.niceFloat
that skips them.
- ✳️ Added:
Fuzz.listOfLength : Int -> Fuzzer a -> Fuzzer (List a)
- ✳️ Added:
Fuzz.listOfLengthBetween : Int -> Int -> Fuzzer a -> Fuzzer (List a)
Common helpers for list of given length.
-- OK
Fuzz.intRange 0 8
|> Fuzz.andThen (\length -> Fuzz.listOfLength length item)
-- BETTER
Fuzz.listOfLengthBetween 0 8 item
- ✳️ Added:
Fuzz.weightedBool : Float -> Fuzzer Bool
- ✳️ Added:
Fuzz.lazy : (() -> Fuzzer a) -> Fuzzer a
- ✳️:warning: Added:
Fuzz.fromGenerator : Generator a -> Fuzzer a
Miscellaneous helpers.
Fuzz.weightedBool
behaves like a biased coin: it will give you True
with the probability p
you give it:
Fuzz.examples 10 (Fuzz.weightedBool 0.8)
--> [True,True,True,True,False,True,True,False,True,True] : List Bool
Fuzz.lazy
is helpful when defining recursive fuzzers for your ASTs. If you'll need it you'll know!
Fuzz.fromGenerator
is an escape hatch. Please don't use Random.Generator
as a Fuzzer
, but the simplification process will be effectively turned off: it will have no idea what's a simpler value. It will always result in better results if you create proper fuzzers instead.
- ❌ Removed:
Expect.true : String -> Bool -> Expectation
- ❌ Removed:
Expect.false : String -> Bool -> Expectation
The Expect.true
and Expect.false
functions were a solution that was too easy to reach for while better solutions were possible.
If you're certain checking a Boolean really is what you want, you can still achieve the same with your current error messages via a combination of Expect.equal
and Expect.onFail
.
-- BEFORE
yourBoolean
|> Expect.true "Your custom failure message"
-- AFTER
yourBoolean
|> Expect.equal True
|> Expect.onFail "Your custom failure message"
- ❌ Removed:
Test.Runner.Failure.format : String -> Reason -> String
This function has been deprecated for a long time (1.0.0
–1.2.2
) with the following message:
DEPRECATED. In the future, test runners should implement versions of this that make sense for their own environments.
We've added new Expectation
s for Html
tests that allow you to check whether an event handler will stop propagation or prevent default behaviour of the event or not.
- ✳️ Added:
Test.Html.Event.expectNotPreventDefault : Event msg -> Expectation
- ✳️ Added:
Test.Html.Event.expectNotStopPropagation : Event msg -> Expectation
- ✳️ Added:
Test.Html.Event.expectPreventDefault : Event msg -> Expectation
- ✳️ Added:
Test.Html.Event.expectStopPropagation : Event msg -> Expectation
We've made the internal naming change from shrink
to simplify
, and this touched the low-level helpers inside Test.Runner
. Due to reimplementation of the underlying simplification process the API of the two functions has changed somewhat too, but all the intended usecases should still be doable. Let us know if you have some special needs!
- ✍️ Renamed:
-Test.Runner.Shrinkable a
+Test.Runner.Simplifiable a
- ✍️ Changed:
-Test.Runner.shrink : Bool -> Shrinkable a -> Maybe ( a, Shrinkable a )
+Test.Runner.simplify : (a -> Expectation) -> ( a, Simplifiable a ) -> Maybe ( a, Simplifiable a )
- ✍️ Changed:
-Test.Runner.fuzz : Fuzzer a -> Result String (Generator ( a, Shrinkable a ))
+Test.Runner.fuzz : Fuzzer a -> Generator (Result String ( a, Simplifiable a ))