![heart of the beholder heart of the beholder](https://i.pinimg.com/originals/e4/0d/7e/e40d7ef84edbf610fc7fcc7a96bcb853.jpg)
The argument value is the value we're trying to shrink - so all returned candidates must be smaller than value.
![heart of the beholder heart of the beholder](https://quotefancy.com/media/wallpaper/3840x2160/40469-H-G-Wells-Quote-Beauty-is-in-the-heart-of-the-beholder.jpg)
For lists of Person s that function looks like this: def shrink_list_of_person(value: list) -> Iterable]: We'll assume we'll have an oracle available that for any given value comes up with candidate smaller values. Lather, rinse, repeat until we can't come up with candidate smaller values. If the test still fails with the smaller value, we have succeeded in shrinking the input, and can try to shrink that smaller input further. Since we have randomly found an input for which the test fails, we can re-run the test with candidate input values that are in some way smaller than the failing input value.
![heart of the beholder heart of the beholder](https://img.divicast.com/xxrz/1200x600/209/96/af/96af265b0993ccefce1eb59cc5610475/96af265b0993ccefce1eb59cc5610475.jpg)
The strategy is, in a nutshell, iterative improvement. QuickCheck's approach is relatively easy to explain, but can be tricky to understand. Not the kind of shrink you pour your heart out to Here we'll look at the original proposal first implemented in QuickCheck, and we'll cover others in following posts. There are quite a few different approaches to shrinking in various PBT libraries, each with their own trade-offs. Shrinking won't always work this well, but nonetheless is surprisingly effective in practice. The end result is a perfect shrink - there's no way to make the value smaller while still failing the test! Indeed, the number of elements must be at least two, the names must be minimally different, and the ages must be minimally different as well. The approach for shrinking we'll implement in this post is methodical: first the number of elements in the list is reduced to two, then the remaining Person values are individually shrunk by making their fields smaller. Usually, real PBT libraries will present the original randomly generated argument, and the final smallest value, but I've printed all the intermediate "shrinks" here for reference. Shrinking: gave up - smallest arguments found (,) Here's what that process looks like after we're through with this post: > test(prop_wrong_sort_by_age) To make the test input smaller, we repeatedly re-run the test with "smaller" input values, and check if the test still fails. Shrinking starts when we find a failing random test input. Imagine Person is a more realistic class with more than a handful of fields, some of which may be other classes with their own fields as well. We could debug with this input value, but it's quite unwieldy, even for this toy example. Still it's not clear what the problem is - besides that the result is not sorted by age. We could try and see what wrong_sort_by_age 's output is on the randomly generated input: > wrong_sort_by_age() That's a good start, but it doesn't really give us any idea of where the problem is. Our implementation dutifully finds a list for which this property fails: > test(prop_wrong_sort_by_age)įail: at test 0 with arguments (,). This bug means the result is sorted by name first, then by age. Lambda persons_in: is_valid(persons_in, wrong_sort_by_age(persons_in)) Now let's see what happens when we inject a bug in the implementation: def wrong_sort_by_age(people: list) -> list: Let's take a sneak peek ahead at what we'll implement in this post.Īs a reminder, we were testing a function sort_by_age which takes as input a list of Person objects and is simply supposed to sort them by their age field. Legend has it that if you keep walking down this gallery, you eventually shrink to the quantum realm.