[Haskell-cafe] Replacing IO with pure values

37 views
Skip to first unread message

martin

unread,
Nov 24, 2014, 10:27:59 AM11/24/14
to haskel...@haskell.org
Hello all,

in order to nail down my problem state in "Replacing the world in a State Machine with IO", I'd like to answer a much
simpler question:

If I have function which reads IO and I want to test this function by passing it a series of pure values, how would I do
this? I suppose I can do this by splitting my function in two, such that the outer function does IO and the inner is a
pure function. Is there any other way?



_______________________________________________
Haskell-Cafe mailing list
Haskel...@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Corentin Dupont

unread,
Nov 24, 2014, 11:25:21 AM11/24/14
to martin, haskel...@haskell.org
You cannot "Unit" test functions that performs, because by definition a unit test is about testing an isolated piece of code that give the same result for the same input, i.e. pure.
In my programs I separate the non-IO parts for the IO parts, so that I can test the non-IO parts.
The IO parts are mainly the GUI parts of my apps, which I test manually.

Now it should be possible to trick the IO monad by supplying always the same user input to your function, but I don't know how to do that.


Corentin Dupont

unread,
Nov 24, 2014, 11:26:32 AM11/24/14
to martin, haskel...@haskell.org
You cannot "Unit" test functions that performs IO, because by definition a unit test is about testing an isolated piece of code that give the same result for the same input, i.e. pure.
In my programs I separate the non-IO parts for the IO parts, so that I can test the non-IO parts.
The IO parts are mainly the GUI parts of my apps, which I test manually.

Now it should be possible to trick the IO monad by supplying always the same user input to your function, but I don't know how to do that.t performs, because by definition a unit test is about testing an isolated piece of code that give the same result for the same input, i.e. pure.

David Thomas

unread,
Nov 24, 2014, 3:03:35 PM11/24/14
to Corentin Dupont, haskel...@haskell.org
Depending on your scope, one thing you could do is replace the things
that operate against IO with functions that operate against some other
interface, and then provide an IO-based implementation and a pure
implementation of that interface.

That works best where the IO activity is fairly simple; of course,
when that's true you can often find ways to factor it out and just get
yourself a pure function to test anyway. It works worst when you're
calling out into complicated libraries that live in IO. There may be
a sweet spot between these two where it would be a good fit.

martin

unread,
Nov 24, 2014, 3:46:25 PM11/24/14
to haskel...@haskell.org
Thanks to all. This helped a lot.

Paul Brauner

unread,
Nov 25, 2014, 6:57:05 AM11/25/14
to martin, haskel...@haskell.org
There's https://hackage.haskell.org/package/IOSpec which provides a pure implementation of the IO monad. Once you're done testing your code you can import Test.IOSpec.Surrogate which declares type IOSpec f a = IO a.

Would be a nice use case for ML-style modules btw :)

Julian Arni

unread,
Nov 25, 2014, 8:56:00 AM11/25/14
to haskell-cafe, martin.d...@web.de
Besides, the aforementioned IOSpec, I would also suggest taking a look at "Purify Code Using Free Monads" [0], which gives a nice introduction to a general approach. If you're faced with library functions (i.e., ones you haven't written yourself) that do IO, it can become a little onerous to mock them all, though. In case the IO you're interested in testing is mostly stdin/stderr/stdout related, you could also look at the 'silently' package [1]. Finally, the 'knob' package [2] allows you to use in-memory file handles, so it helps if your tack ends up being passing Handles to pure functions (so that in the executable, those handles are stdin/stderr/stdout + files etc, but in the test they're just in-memory handles provided by 'knob'). 'System.Environment' has 'withArgs', which provides for the case of testing command-line args.

[0] http://www.haskellforall.com/2012/07/purify-code-using-free-monads.html
[1] https://hackage.haskell.org/package/silently-1.2.4.1
[2] http://hackage.haskell.org/package/knob-0.1.1/docs/Data-Knob.html

Jeffrey Brown

unread,
Nov 26, 2014, 2:59:50 PM11/26/14
to Julian Arni, haskell-cafe
It seems like it ought to be possible to manually test some IO once, record the user input, and automate future testing by using that record. Is it not? 

Reply all
Reply to author
Forward
0 new messages