Paul Nikonowicz
Paul Nikonowicz

Reputation: 3903

Testing IO in F#

Given i have the IO function:

// this can either be IO or some other side effect 
//that makes the function less pure
printf "HI"

I want to test that IO was called correctly. An imperative solution for testing that IO was called correctly would be to wrap the IO statement in an object, mock the object, pass the object in using dependency injection, and verify the correct method was called with the correct parameters. I wonder if instead of using dependency injection to test F#, a better way would be checking the output of the function (by asserting that the correct value or function is returned) and stub out the IO call; therefore making the function pure again by eliminating the side effect of an IO call.

I am considering wrapping all IO in a special module like so.

let MyPrint print statement = print statement ; statement

so that i can stub out the IO function and assert in my tests that the correct operation occurred like so:

code under test:

let PrintHi = fun(print) -> MyPrint print "HI"
let DoNothing = fun(print) -> ()

let DoIf conditional = 
     if conditional then PrintHi
     else DoNothing

FsUnit:

[<Test>] member test.
     let printStub value = ()

     ``Test Hi Is Printed When TRUE`` ()=
          let testedFunc = DoIf true
          testedFunc(printStub) |> should equal PrintHi(printStub)

Is this a good way to test IO side effects? Is there a better way? Please keep in mind that my goal is to test any IO, not just a print statement.

Upvotes: 3

Views: 542

Answers (3)

Mauricio Scheffer
Mauricio Scheffer

Reputation: 99750

Generally speaking, you'll want to separate pure code from impure (side-effecting) code; and keep code as pure as possible.

I recommend reading these articles about it, they're written for other functional languages but the code they use is simple and the concepts are well explained and can be easily applied in F# (and many other languages for that matter):

Upvotes: 7

Random Dev
Random Dev

Reputation: 52290

I guess you don't really want to check if printf works as expected (to you?) - I think you want to know if there is some more functional way than DI to get testable results.

The answer is twofold: First: F# is a mixed languague with a big OOP part - so yes I would do your standard DI pattern with interfaces and all that. Second: instead of using this pattern you can allways use higher-order functions to pass in functions that does for example the IO - in your case something like

let myFunctionUsingIO (printer : string -> unit) (whateverparamsYouNeed) = ...

and then test this by passing a printer that Asserts whatever your requirements are - but in the end thats the same as having a interface with only one (unnamed) method - so the difference is very small.

PS: if you only interessted in the return value - just do normal unit-testing - if you write your functions pure there is no need to test anything different, but then your example was ... well poor, because printf is the opposite of pure...

Upvotes: 3

Lee
Lee

Reputation: 144196

You could write a wrapper function which temporarily redirects stdout during the call to a function and returns the written values along with the function result:

let testPrintf f arg =
    let oldOut = System.Console.Out
    use out = new System.IO.StringWriter()
    System.Console.SetOut(out)
    let res = f arg
    System.Console.SetOut(oldOut)
    (res, out.GetStringBuilder().ToString())

Upvotes: 4

Related Questions