0

I'm trying to write a test for a small lib I'm writing that (essentially) just logs to the console.

Is there a way to mock function like this is F#?

example: in src/Lib/Lib.fs

module Lib

let print msg = printfn "Your message is: %s" msg

then, in test/LibTest/Lib.fs

module LibTest

open NUnit.Framework
open FsUnit

[<Test>]
let ``should print what I expect``() =
  print "test" |> should equal "Your message is: test"

Note: I'm aware that currently print returns unit - I am looking for a way to make assertions about what is passed to printfn (or, more ideally, what is sent to stdout, which relies less on implementation).

I've tried directly assigning a mock function to Printf.printfn to no avail (obviously, when I think about it). Is it possible to capture the output to the console? Or to mock the printfn function (it's an implementation detail, but I can live with that).

David
  • 1,326
  • 9
  • 16
  • The signature of your function `print` is `'a -> unit`, so result won't be equal to your message. Maybe you're should use `sprintf` instead, which builds a string and returns it instead of printing to Console. – kagetoki Oct 15 '18 at 05:52
  • @kagetoki i know that it returns unit. But, as my library code actually prints to the console, I'd like to mock `printfn` in some way so that I can make some assertions about what will be printed. – David Oct 15 '18 at 06:24

1 Answers1

0

This is what I use for testing:

module StdOut =
    let stdout = System.Text.StringBuilder()
    let out (s:string) = stdout.Append s |> ignore
    let call func parm =
        stdout.Clear() |> ignore
        func parm, stdout.ToString()
    let run f p = call f p |> snd

let inline  (|>!) v f   = f v ; v
/// used to capture print outs for testing. like printfn
let printoutfn  fmt = fmt |> Printf.ksprintf (fun s -> s + "\n" |>! printf "%s"|> StdOut.out)
/// silent version of printoutfn
let printoutfns fmt = fmt |> Printf.ksprintf (fun s -> s + "\n"                |> StdOut.out)
let printout      v = printoutfn "%A" v

The code I am testing calls printoutfn or printoutfns which is the silent version.

To test the output I do it like this:

let print msg = printoutfn "Your message is: %s" msg

[<Test>] 
let ``should print what I expect``() =
      StdOut.run print "test" |> should equal "Your message is: test\n"

Notice how it includes the newline character: \n.

There are two invocations: StdOut.call func param and StdOut.run func param the former returns the function value and the output, the latter only returns the output

AMieres
  • 4,944
  • 1
  • 14
  • 19