[Haskell-cafe] property testing with context

Ben Franksen ben.franksen at online.de
Mon Oct 22 09:22:53 UTC 2018


Hi Ollie

This is fantastic! It's certainly much more than I hoped for.

I haven't looked into the details of your plugin yet, but from your
description it looks as if this is currently geared towards "classical"
unit tests. Perhaps this is not a problem: I think quickcheck & co
report exceptions as a failed property, and if they don't we'll just
have to catch the exception, print it and return False.

I will definitely try it out and report back.

Cheers
Ben

Am 19.10.2018 um 23:25 schrieb Oliver Charles:
> Perhaps the work in my "assert-explainer" project is relevant here -
> https://github.com/ocharles/assert-explainer.
> 
> The idea is your tests should just be writing "assert
> someArbitraryExpressionEvaluatingToBool", and then having some
> compiler magic recover the context for you when it goes wrong. To cite
> my own README:
> 
> 
> You write:
> 
>     assert (length xs == 4)
> 
> No need for lots of special assertEqual etc functions.
> 
> When the assertion fails, you will get much more context:
> 
> ✘ Assertion failed!
>     length xs == 4 /= True (at Test.hs:18:12-25)
> 
>   I found the following sub-expressions:
>     - length xs = 3
>     - xs = [1,2,3]
> 
> 
> This is done via a GHC source plugin that rewrites "assert (length xs
> == 4)" into an expression that - if False - prints much more
> information.
> 
> It's very early days for this plugin, but the goal is to reach parity
> with https://docs.pytest.org/en/latest/example/reportingdemo.html#tbreportdemo.
> 
> Hope this helps,
> Ollie
> On Fri, Oct 19, 2018 at 8:20 AM Ben Franksen <ben.franksen at online.de> wrote:
>>
>> Hi everyone
>>
>> it seems to be the season for new variations on the "property testing"
>> theme, so I would like to chime in... not to announce a new library,
>> sadly, but with a rough idea how the existing ones could perhaps be
>> improved, based on practical experience in Darcs.
>>
>> The problem I have is that there is a tension between
>>
>> (a) stating a property in a clear and simple way, so its code doubles as
>> a formal specification
>>
>> and
>>
>> (b) writing the property in such a way that when it fails, the reported
>> value(s) give enough information about the context to be useful for
>> finding the cause of the problem.
>>
>> Let me give an example to demonstrate what I mean.
>>
>> There is a simple law that says if a sequential pair of patches A;B
>> commutes to B';A' then B';A' commutes back to A;B. In code this looks
>> (more or less) like this:
>>
>> prop_recommute :: Commute p => (p :> p) wA wB -> Bool
>> prop_recommute (x:>y)
>>   | Just (y':>x') <- commute (x:>y) =
>>       case commute (y':>x')of
>>         Just (x'':>y'') -> x==x'' && y==y''
>>         Nothing -> False
>>   | otherwise = True
>>
>> This is a bit more verbose than the informal spec but still quite readable.
>>
>> Now suppose this property fails. So quickcheck reports the counter
>> example pair (X:>Y) for some X and Y. But that isn't too useful in
>> itself. We'd like to know a bit more:
>>
>>  * did the second commute fail?
>>  * or did it succeed but x/=x'' or y/=y''?
>>  * and in the latter case, which of the two?
>>
>> So in practice our property code looks more like this:
>>
>> prop_recommute :: (ShowPatch p, Commute p) => (p :> p) wA wB -> Bool
>> prop_recommute (x :> y)
>>   | Just (y' :> x') <- commute (x :> y) =
>>       case commute (y' :> x') of
>>         Nothing ->
>>           failed (redText "failed, where x" $$ displayPatch x $$
>>                   redText ":> y" $$ displayPatch y $$
>>                   redText "y'" $$ displayPatch y' $$
>>                   redText ":> x'" $$ displayPatch x')
>>         Just (x'' :> y'') ->
>>           if y'' /= y
>>             then
>>               failed (redText "y'' =/\\= y failed, where x" $$
>>                       displayPatch x $$
>>                       redText ":> y" $$ displayPatch y $$
>>                       redText "y'" $$ displayPatch y' $$
>>                       redText ":> x'" $$ displayPatch x' $$
>>                       redText "x''" $$ displayPatch x'' $$
>>                       redText ":> y''" $$ displayPatch y'')
>>             else
>>               if x'' /= x
>>                 then
>>                   failed (redText "x'' /= x, where x" $$
>>                           displayPatch x $$
>>                           redText ":> y" $$ displayPatch y $$
>>                           redText "y'" $$ displayPatch y' $$
>>                           redText ":> x'" $$ displayPatch x' $$
>>                           redText "x''" $$ displayPatch x'' $$
>>                           redText ":> y''" $$ displayPatch y'')
>>                 else True
>>   | otherwise = True
>>
>> Now this code tells us exactly what went wrong when the property fails
>> but it is ugly and repetitive and the additional code obscures the
>> simple logical content. So this is no longer quite as suitable for a
>> (human readable) formal spec.
>>
>> I wonder if displaying
>>
>> (1) all relevant contextual variables and
>> (2) "where in the code it fails"
>>
>> could be automated away, somehow. I guess this is not trivial and may
>> require syntactic analysis, so perhaps expecting a /library/ to solve
>> the problem is unrealistic, except perhaps by using Template Haskell.
>> I'd also go with a separate tool that extracts properties from a module
>> and enriches them with code that displays the additional information.
>>
>> Tackling this problem might be an interesting theme for a master
>> thesis... ;-)
>>
>> Cheers
>> Ben
>>
>> _______________________________________________
>> Haskell-Cafe mailing list
>> To (un)subscribe, modify options or view archives go to:
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> Only members subscribed via the mailman list are allowed to post.
> _______________________________________________
> Haskell-Cafe mailing list
> To (un)subscribe, modify options or view archives go to:
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> Only members subscribed via the mailman list are allowed to post.
> 




More information about the Haskell-Cafe mailing list