35

Is there something in Scalatest that will allow me to test the output to the standard out via a println statement?

So far I've mainly been using FunSuite with ShouldMatchers.

e.g. how do we check the printed output of

object Hi {
  def hello() {
    println("hello world")
  }
}
Luigi Plinge
  • 50,650
  • 20
  • 113
  • 180

3 Answers3

94

If you just want to redirect console output for a limited duration, use the withOut and withErr methods defined on Console:

val stream = new java.io.ByteArrayOutputStream()
Console.withOut(stream) {
  //all printlns in this block will be redirected
  println("Fly me to the moon, let me play among the stars")
}
Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
  • 7
    This one is way better, no need to restructure your program for tests. – BorisOkunskiy Feb 26 '13 at 07:11
  • should have been the accepted answer, code should be written in a way its testable, but adding traits/interfaces specifically for tests is not something i am lot comfortable with. – dhruvsharma Nov 28 '22 at 09:52
31

The usual way to test print statements on the console is to structure your program a bit differently so that you can intercept those statements. You can for example introduce an Output trait:

  trait Output {
    def print(s: String) = Console.println(s)
  }

  class Hi extends Output {
    def hello() = print("hello world")
  }

And in your tests you can define another trait MockOutput actually intercepting the calls:

  trait MockOutput extends Output {
    var messages: Seq[String] = Seq()

    override def print(s: String) = messages = messages :+ s
  }


  val hi = new Hi with MockOutput
  hi.hello()
  hi.messages should contain("hello world")
Eric
  • 15,494
  • 38
  • 61
  • You need to add `override` to MockOutput – Amir Raminfar Aug 28 '11 at 18:20
  • 1
    I like this solution a lot, @Eric is there a way to do this without having to extend `Output`. I feel `extending` a trait, where that trait is not needed in the first place, is a hack. It would make sense if the trait was already needed and we created a test impl. – Amir Raminfar Aug 28 '11 at 18:29
  • 3
    The only other way to avoid extending a trait is to do what Kevin or Matthieu are advising to. That being said, I have the philosophy that building your software so that it is testable is a good design decision. When you pursue that thought you go all the way to introducing traits for *all* your IO / external systems interactions. – Eric Aug 28 '11 at 23:18
  • @eric I wouldn't say that I was **advising** redirection as a good way to write unit tests, just pointing out that it's a possibility if you e.g. needed to retrofit tests to existing code and wanted to be minimally invasive. I'd hate to change untested code any more than was *strictly* necessary; even if it *is* for the purpose of making it easier to add said tests – Kevin Wright Feb 01 '14 at 22:52
  • `override` useful but not necessary – juanchito Feb 25 '20 at 01:26
6

You can replace where println writes to by using Console.setOut(PrintStream)

val stream = new java.io.ByteArrayOutputStream()
Console.setOut(stream)
println("Hello world")
Console.err.println(stream.toByteArray)
Console.err.println(stream.toString)

You can obviously use any type of stream you want. You can do the same sort of thing for stderr and stdin with

Console.setErr(PrintStream)
Console.setIn(PrintStream)
Matthew Farwell
  • 60,889
  • 18
  • 128
  • 171