4

Is it common practice to unit test stdIn/stdOut? If so, then how would you test something like this:

import scala.io.StdIn._

object Test {

    def main(args: Array[String]) = {

        println("Please input your text. Leaving an empty line will indicate end of the input.")

        val input = Iterator.continually(readLine()).takeWhile(_ != "").mkString("\n")

        val result = doSomethingWithInput(input)

        println("Result:")
        println(result)

    }

}

I'm normally using ScalaTest if that makes any difference.

Caballero
  • 9,738
  • 18
  • 89
  • 151

3 Answers3

6

Since Scala uses the standard Java stream(System.out, System.in) behind the scenes you can test it, by replacing the standard streams with your custom stream which you can further inspect. See here for more details.

In reality though I'd primarily focus making sure doSomethingWithInput is fully tested and maybe follow up with testing of the input reading (to make sure the stop condition and input string construction work as expected).

If you've already tested the value that you're going to println then making sure it has been send to the console stream gives a very little benefit for a lot of effort. Moreover such test cases will be the pain to maintain going forward. As always it depends on your use case, but in majority of cases I'd just refrain from testing it.

Community
  • 1
  • 1
Norbert Radyk
  • 2,532
  • 14
  • 23
  • Note that the approach to redirect the standard streams for Java referenced above does not work consistently for Scala. See [here](http://www.scala-lang.org/old/node/9443) for why you should use, for example, `Console.withOut` or `Console.setOut` (deprecated in 2.11). See also [this answer](http://stackoverflow.com/a/7219813/3900879). – Rusty Shackleford Jul 15 '15 at 17:56
3

Console object provides withIn and withOut methods enabling temporary redirection of stdin and stdout. Here is a working example which tests method vulcanIO which both reads and prints to stdin/stdout:

import java.io.{ByteArrayOutputStream, StringReader}
import org.scalatest._
import scala.io.StdIn

class HelloSpec extends FlatSpec with Matchers {
  def vulcanIO(): Unit = {
    println("Welcome to Vulcan. What's your name?")
    val name = StdIn.readLine()
    println("What planet do you come from?")
    val planet = StdIn.readLine()
    println(s"Live Long and Prosper , $name from $planet.")
  }

  "Vulcan salute" should "include , name, and planet" in {
    val inputStr =
      """|Jean-Luc Picard
         |Earth
      """.stripMargin
    val in = new StringReader(inputStr)
    val out = new ByteArrayOutputStream()
    Console.withOut(out) {
      Console.withIn(in) {
        vulcanIO()
      }
    }
    out.toString should (include ("") and include ("Jean-Luc Picard") and include ("Earth"))
  }
}

Note how redirection happens inside

Console.withOut(out) {
  Console.withIn(in) {
    vulcanIO()
  }
}

and how we assert on the output stream out

out.toString should (include ("") and include ("Jean-Luc Picard") and include ("Earth"))
Mario Galic
  • 41,266
  • 6
  • 39
  • 76
0

I would change doSomethingWithInput to take a BufferedSource as parameter so you can write your unit tests with any source stream not just stdin

spew
  • 44
  • 1
  • But `doSomethingWithInput` takes `String` as a parameter (the result of `mkString("\n") call). IMHO its a bit easier to unit test string manipulations with `String` parameter rather than `BufferedSource`. – Norbert Radyk Jun 15 '14 at 19:39