elm-explorations/test 2.0.0 upgrade guide
Table of Contents:
There are many changes in the
- many helpers added to achieve common patterns in an optimized way
🔥(all shrinkingsimplifying 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:
Fuzz.custom : Generator a -> Shrinker a -> Fuzzer a
There are now no more
Shrinkers! All simplification is automatic. This is thanks to the integrated shrinking approach popularized by the Hypothesis library.
Fuzz.custom gone and with
Fuzz.andThen added, it should now be always possible to mirror your
Fuzzers. Take a look at the Random to Fuzz translation section for examples and more help.
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
-Fuzz.tuple : ( Fuzzer a, Fuzzer b ) -> Fuzzer ( a, b ) +Fuzz.pair : Fuzzer a -> Fuzzer b -> Fuzzer ( a, b )
-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.
Fuzz.andThen : (a -> Fuzzer b) -> Fuzzer a -> Fuzzer b
Fuzz.filter : (a -> Bool) -> Fuzzer a -> Fuzzer a
Fuzz.sequence : List (Fuzzer a) -> Fuzzer (List a)
Fuzz.traverse : (a -> Fuzzer b) -> List a -> Fuzzer (List b)
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.andThen is available.
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!
Fuzz.oneOfValues : List a -> Fuzzer a
Fuzz.frequencyValues : List ( Float, a ) -> Fuzzer a
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 ]
Fuzz.asciiChar : Fuzzer Char
Fuzz.asciiString : Fuzzer String
Fuzz.asciiStringOfLength : Int -> Fuzzer String
Fuzz.asciiStringOfLengthBetween : Int -> Int -> Fuzzer String
Fuzz.stringOfLength : Int -> Fuzzer String
Fuzz.stringOfLengthBetween : Int -> Int -> Fuzzer String
String helpers. Note that the
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
Fuzz.intAtLeast : Int -> Fuzzer Int
Fuzz.intAtMost : Int -> Fuzzer Int
Fuzz.uniformInt : Int -> Fuzzer Int
Integer fuzzers prefer generating smaller values by default. We've introduced functions
Fuzz.intAtMost which use
Random.maxInt as their limit values, removing the need for you to import 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]
Fuzz.floatAtLeast : Float -> Fuzzer Float
Fuzz.floatAtMost : Float -> Fuzzer Float
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
Unfortunately there was no easy way to keep this behaviour for
Fuzz.floatRange and certain intervals inside
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
-Infinity. If you don't want these generated, there's
Fuzz.niceFloat that skips them.
Fuzz.listOfLength : Int -> Fuzzer a -> Fuzzer (List a)
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
Fuzz.weightedBool : Float -> Fuzzer Bool
Fuzz.lazy : (() -> Fuzzer a) -> Fuzzer a
Fuzz.fromGenerator : Generator a -> Fuzzer a
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.
Expect.true : String -> Bool -> Expectation
Expect.false : String -> Bool -> Expectation
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
-- BEFORE yourBoolean |> Expect.true "Your custom failure message" -- AFTER yourBoolean |> Expect.equal True |> Expect.onFail "Your custom failure message"
Test.Runner.Failure.format : String -> Reason -> String
This function has been deprecated for a long time (
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
Html tests that allow you to check whether an event handler will stop propagation or prevent default behaviour of the event or not.
Test.Html.Event.expectNotPreventDefault : Event msg -> Expectation
Test.Html.Event.expectNotStopPropagation : Event msg -> Expectation
Test.Html.Event.expectPreventDefault : Event msg -> Expectation
Test.Html.Event.expectStopPropagation : Event msg -> Expectation
We've made the internal naming change from
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!
-Test.Runner.Shrinkable a +Test.Runner.Simplifiable a
-Test.Runner.shrink : Bool -> Shrinkable a -> Maybe ( a, Shrinkable a ) +Test.Runner.simplify : (a -> Expectation) -> ( a, Simplifiable a ) -> Maybe ( a, Simplifiable a )
-Test.Runner.fuzz : Fuzzer a -> Result String (Generator ( a, Shrinkable a )) +Test.Runner.fuzz : Fuzzer a -> Generator (Result String ( a, Simplifiable a ))