How to use a String like it’s a File in Scala (such as in testing)

This is an excerpt from the 1st Edition of the Scala Cookbook. This is Recipe 12.7, “How to use a String like it's a file in Scala.”

Problem

Typically for the purposes of testing, you want to pretend that a String is a file in your Scala code.

Solution

Because Scala.fromFile and Scala.fromString both extend scala.io.Source, they are easily interchangeable. As long as your method takes a Source reference, you can pass it the BufferedSource you get from calling Source.fromFile, or the Source you get from calling Source.fromString.

For example, the following method takes a Source object and prints the lines it contains:

import io.Source

def printLines(source: Source) {
    for (line <- source.getLines) {
        println(line)
    }
}

It can be called when the source is constructed from a String:

val s = Source.fromString("foo\nbar\n")
printLines(s)

It can also be called when the source is a file:

val f = Source.fromFile("/Users/Al/.bash_profile")
printLines(f)

Discussion

When writing unit tests, you might have a method like this that you’d like to test:

package foo

object FileUtils {
    def getLinesUppercased(source: io.Source): List[String] = {
        (for (line <- source.getLines) yield line.toUpperCase).toList
    }
}

As shown in the following ScalaTest tests, you can test the getLinesUppercased method by passing it either a Source from a file or a String:

package foo

import org.scalatest.{FunSuite, BeforeAndAfter}
import scala.io.Source

class FileUtilTests extends FunSuite with BeforeAndAfter {
    var source: Source = _

    after { source.close }

    // assumes the file has the string "foo" as its first line
    test("1 - foo file") {
        source = Source.fromFile("/Users/Al/tmp/foo")
        val lines = FileUtils.getLinesUppercased(source)
        assert(lines(0) == "FOO")
    }

    test("2 - foo string") {
        source = Source.fromString("foo\n")
        val lines = FileUtils.getLinesUppercased(source)
        assert(lines(0) == "FOO")
    }

}

In summary, if you’re interested in making your method easily testable with a String instead of a file, define your method to take a Source instance.

See Also