Is there a way using scalacheck or perhaps scalatest to assert that calling a function (or method) prints a particular message? Or a message matching a certain regular expression?
println()? Not that I can think of – it’s a very pure side-effect. If you need this, I would recommend providing an alternate “print” function that you can intercept…
For example does println print to stdout? Can I open a file for output, rebind stdout to that file port, call the function in question, restore stdout, close the file port, and read the file content into a string, and make assertions about that string?
Yes, println writes to standard output. It is short for Console.println and can be reassigned via Console.setOut.
But using such a strategy for unit tests won’t work so well, because multiple unit tests are often run in parallel. Unit tests should be self-contained and not mess with global settings.
You can use
Console.withOut to rewire stdout in the scope of a single piece of code / thunk. It uses a
ThreadLocal variable (
DynamicVariable) under the hood.
scala> import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream scala> def foo(i: Int) = println(s"printing $i") foo: (i: Int)Unit scala> val out = new ByteArrayOutputStream out: java.io.ByteArrayOutputStream = scala> Console.withOut(out)(foo(42)) scala> out.toString res8: String = "printing 42 "
ThreadLocals will of course break under async/ parallel code, but most of the code is synchronous and serial, so it should usually work.
It seems like something which should be built into scalatest. I’ve built this in to several testing frameworks I’ve written during my career, although never in Scala.
My use case was pretty innocent. I had given my students an assignment to write hello world. The challenge was for them to figure out how to create all the boilerplate to make it work in IntelliJ. I wanted to insert a test case which asserts that their function indeed printed “hello world”.
As innocent as it may seem, redirecting global standard output for automatic inspection is not something that’s casually done. Global standard output is a mutable state so it doesn’t work deterministically in multithreaded environment. ScalaTest runs tests concurrently (i.e. by default different specs are run in parallel, while tests within one spec are run serially) so output from different specs is intertwined anyway. Solutions that do not mess with standard output are definitelly better. Create your own PrintStream (or PrintWriter) and pass it to methods that your students implement as an explicit argument.