<div dir="ltr"><div>Thanks for the replies,</div><div><br></div><div>I ended up generating random sequence of events and then checking some properties/invariants on the final state. I realized that in other languages I usually expressed these properties by writing assertions inside the code (e.g. by using the assert() function in C/C++ or throwing a RuntimeException in Java).</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Feb 5, 2017 at 5:41 AM, Robin Palotai <span dir="ltr"><<a href="mailto:palotai.robin@gmail.com" target="_blank">palotai.robin@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>A very powerful idea is described in Testing Monadic Code With QuickCheck[1] (or a pdf version at [2]). I think the ideas apply to non-monadic (or non IO-like monadic) code as well.<br></div><div><div><br></div><div>The basic idea is to generate a sequence of events as test input, like<br><br></div><div>    events = [EventA, EventA, EventC, EventB]<br><br></div><div>and execute the corresponding actions (starting from an initial state). Now you can make assertions on the final state S and events that happened.<br><br></div><div>Some ideas for your case:<br></div><div>- If EventB happened, S must be foo<br></div><div>- If EventC never happened, S must be bar (note that negative event tests are very powerful)<br></div><div>- S must have baz=0 only if the count of EventAs is same as count of EventBs after the first EventC<br>- ...<br><br></div><div>You can also compare final states resulting from different event sequences:<br></div><div>- if E1 is a sequence with result S1, sticking EventD anywhere in E1 must also result in S1<br>- ...<br></div><div><br></div><div>The paper goes into details about generating a valid event sequence, if there are restrictions about in which state can a given event happen. <br></div><div><br></div><div>Section 3 & 4 are about testing that (the effect of) two given action sequences are equal, independent of previous/subsequent actions. This is useful if you don't only handle events, but can do some custom actions on your state. Like you can test that "foo >> barBaz" is always the same as "foo >> bar >> foo >> unicorn".<br><br></div><div>Section 10-12 bring an other example. Section 13 shows a way to more easily generate a valid action sequence, but only in case you have a parallel abstract (pure) implementation, which is not often the case (except algos), and maybe this is really specific to IO/ST.<br></div><div><br>[1]: <a href="http://www.cse.chalmers.se/~rjmh/Papers/QuickCheckST.ps" target="_blank">www.cse.chalmers.se/~rjmh/<wbr>Papers/QuickCheckST.ps</a><br>[2]: <a href="https://www.researchgate.net/publication/2831386_Testing_Monadic_Code_with_QuickCheck" target="_blank">https://www.researchgate.net/<wbr>publication/2831386_Testing_<wbr>Monadic_Code_with_QuickCheck</a><span class="HOEnZb"><font color="#888888"><br><div class="gmail_extra"><br></div><div class="gmail_extra">Robin<br><br></div></font></span><div><div class="h5"><div class="gmail_extra"><div class="gmail_quote">2017-02-04 22:04 GMT+01:00 Mihai Maruseac <span dir="ltr"><<a href="mailto:mihai.maruseac@gmail.com" target="_blank">mihai.maruseac@gmail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">I'm assuming that each state in your example has something to<br>
differentiate it from the others. That can be expanded to some<br>
properties to test.<br>
<br>
Another thing is that one event can move you from one state to only a<br>
set of states. This can also be encoded as a property.<br>
<br>
Finally, try to see if there are some properties/invariants that hold<br>
when handling an event in a state. Doesn't have to be for all events<br>
and all states, but for those where you can find some it should be<br>
possible to write some tests.<br>
<div><div class="m_-2488863175757613134gmail-h5"><br>
On Sat, Feb 4, 2017 at 12:20 PM, George Boulougaris<br>
<<a href="mailto:gboulougaris@gmail.com" target="_blank">gboulougaris@gmail.com</a>> wrote:<br>
> Hello,<br>
><br>
> I have written a Haskell module that contains functions that operate on some<br>
> state. Let's say that the code looks like this (the actual functions may<br>
> return an actual result instead of `()`, but this is irrelevant to the<br>
> question):<br>
><br>
>     data StateContext = StateContext {<br>
>       -- some records<br>
>     }<br>
><br>
>     handleEventA :: EventA -> State StateContext ()<br>
>     handleEventB :: EventB -> State StateContext ()<br>
>     handleEventC :: EventC -> State StateContext ()<br>
><br>
> As you can imagine the behavior of each function depends on the current<br>
> state. For example `handleEventA >> handleEventB` will not produce the same<br>
> result as `handleEventB >> handleEventA`. So I have several HUnit tests that<br>
> verify the behavior of each function at several states.<br>
><br>
> But now I would like to write more tests, that exercise all functions at all<br>
> possible states (the number of states is finite). Writing them with HUnit<br>
> would be quite labor-itensive, so I thought that using QuickCheck might be<br>
> helpful in that case (I have only used it before for trivial functions).<br>
><br>
> But I cannot see which properties should I test, or what kind of data should<br>
> the test generate. I suspect that the test should generate random sequence<br>
> of events (e.g. `handleEventB >> handleEventC >> handleEventA` etc), but I<br>
> cannot see what properties should be satisfied.<br>
><br>
> Thanks<br>
><br>
><br>
</div></div>> ______________________________<wbr>_________________<br>
> Haskell-Cafe mailing list<br>
> To (un)subscribe, modify options or view archives go to:<br>
> <a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bi<wbr>n/mailman/listinfo/haskell-caf<wbr>e</a><br>
> Only members subscribed via the mailman list are allowed to post.<br>
<span class="m_-2488863175757613134gmail-HOEnZb"><font color="#888888"><br>
<br>
<br>
--<br>
Mihai Maruseac (MM)<br>
"If you can't solve a problem, then there's an easier problem you can<br>
solve: find it." -- George Polya<br>
______________________________<wbr>_________________<br>
Haskell-Cafe mailing list<br>
To (un)subscribe, modify options or view archives go to:<br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bi<wbr>n/mailman/listinfo/haskell-caf<wbr>e</a><br>
Only members subscribed via the mailman list are allowed to post.</font></span></blockquote></div><br></div></div></div></div></div></div>
</blockquote></div><br></div>