test-check 2017-04-27

Yeah, test.check hopes that the public API is sufficient

I assume you see the bug in your code?

Or, why that function doesn't/ can't work

other than my typo with the wrong variable? and the lack of the “AST interpreter” that would drive that protocol? no, not really

at least i assume you have some insight i’m missing

In the fmap str example, you'll be passing a string to a shrinker that expects integers

value is a string, (shrink x ...) wants an integer

i was thinking the value would be whatever value was being wrapped

Somebody somewhere will have to parse the string

not the generated value

i you’d have a tree of alternating generate/shrink calls

the idea was that fmap wouldn’t do any shrinking at all

or at least no extra shrinking

beyond what the wrapped generator does

That's ideal, I'm just not sure how you wrap this up into the test runner in an arbitrarily composable way

i’m not even convinced that it makes sense to combine generating and shrinking one-to-one like this

i just assumed that shrinking would be simpler than generating in that the tree would be a subset of the generator tree

and only isomorphic to it if most of the nodes were the identity-shrinker

for example if you generate an arbitrary number and then filter the even ones, you only want one number shrinker, not two

similarly, for the stateful testing case with shrinking, you almost only want to remove actions, not actually alter the contents of any of those actions

way beyond just generative testing, i’m not a fan of monadic apis in general…. i’d much rather build a generator out of transparent data and then interpret (or compile) that - especially so that i could add more methods beyond just generate/shrink….

but this is way off topic, my apologies

I'm not even sure what sort of tree you're imagining

the tree of generators that is returning a pure value (that is, of course, tree shaped)

The shape of generator composition doesn't necessarily mirror the shape of the data

sure, it’s not 1-to-1, but it has some similarity in general, no?

I've thought about generators-as-data, but seems hard to do bind

bind (well, lexical scope in general) is always the hard part

So test.check has an approach where bind at least works 😛

So you're imagining that the user-facing API for your generators is the protocol of two functions? If so I'm still not seeing how you get around the fmap problem

i’m not trying to propose a new solution

i’m saying that it didn’t match my intuition - which has now been corrected

but the larger problem i have is that, even understanding how it works, i’m not sure how to make effective use of it

let’s take the stack example again

if i have random push/pop operations and it’s going to do shrinking, it’s going to waste a bunch of time pushing or popping smaller integers, when really i want to try fewer pops or fewer pushes

It will shrink each integer straight to 0

Only if that doesn't work does it try other things

So N steps total to accomplish that

But it could also try removing things first, I'm not sure

That depends on which step bind tries first

One downside to this structure is you can't give shrink hints

I think shrinking efficiency is only an issue when generating really large compositions

I suppose I shouldn't say there's no way to give hints; for your example, if it was a problem, you could wrap the integer generator in gen/no-shrink

test.check does this itself for the builtin UUID generator, on the assumption that there's not much to be gained from shrinking a UUID

w.r.t. the earlier question on large-integer boundary testing, that's a more general issue I'm aware of but haven't been sure how clever it should try to be

I've considered rebooting the generators namespace to clean up the API; maybe there could be a partition into lower-level generators and higher-level clever ones

the low level ones do simple easy to understand things, the high level ones do lots of ad-hoc stuff to try to break your code

i’m gonna have a think on all this

thanks again

👍 1

Hey guys, great discussion here. I think the announcement of hedgehog, a new quickcheck-like library for haskell is very relevant: https://www.reddit.com/r/haskell/comments/646k3d/ann_hedgehog_property_testing/. One of its selling points is that it "uses integrated shrinking, so shrinks obey the invariants of generated values by construction [...] an approach to property testing which is also used by QuiviQ's Erlang QuickCheck, Hypothesis, and test.check"

Oh nice - I forgot about that

I see references to a shrink tree in the code. But can't say much more 🙂

@gfredericks: btw I made some progress on the async quick-check. I pushed a draft to my repo today: https://github.com/nberger/test.check/compare/test.check.refactor...refactor-allow-async. I'm working on a defspec-async macro now that would use that stuff and would make the tests much easier to write (for example it "knows" when to call the done from cljs.test/async, which is on :succeeded or :shrunk) so the :step-fn that I'm using in the tests would be gone

@nberger someone made a ticket asking for that

yep, saw it. I've been thinking about this for a long time, the ticket made me go to the keyboard and try to make it work, and I'm quite happy with the result so far

I'm hoping to have a more rounded version soon

cool, let me know and I'll take a look