【Scala】コード上で標準入力・出力のテストを実行したい

Scala Advent Calendar 2019の17日目の記事

どうもアドベントカレンダーってものをつい最近知ったたけちゃです!
面白そうって理由で勢いよくエントリーし参加メンバーをみたら凄い人ばっかりでビビってます^^
Cats使ってみたって記事書いてどやろうとしたんですが、速攻挫折したので最近ハマってる競プロ関連で一つ記事書きます?

背景

AtCoderの自動テストツールを作っていた時のこと

AtCoder(競技プログラミング)ではコードを提出して、間違っているとペナルティーが課せられる(ないものもある)。
そこで公式のサンプルの入力と出力を使い、ローカルでテストをしてから提出すればいいのではと思った。
もちろん公式サイト上でコードのテストはできるのだが、ローカルでテストできる環境が整えば、そのまま自動提出とかもできて色々捗る。。。


まず
提出用コードの形式はobject Main extends App {}
入力データは標準入力で渡される

以上のことを踏まえると。。。

  1. Main関数をコード上で呼び出せる
  2. 呼び出したMain関数に対して任意の標準入力を渡す
  3. Main関数内の標準出力を取得して、正しいかを判断する

これらをやりたいため標準入出力について色々調べてみた。

目標

  1. コード上でMain関数を呼び出す
  2. 標準入力をオーバーライドする
  3. 標準出力をオーバーライドする

コード上でMain関数を呼び出す

これは結構すぐにできた

Main.main(Array.empty)

これでどこからでもMain関数が実行できる
※すでにMain関数があって、それを他のところで呼び出す

標準入力をオーバーライドする

sample inputって文字を標準入力としてMain関数へ渡す。

import java.io.{ByteArrayInputStream, FileInputStream}
import java.nio.charset.StandardCharsets

val str = "sample input"
System.setIn(new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8)))

// 外部ファイルを読み込んで、その内容を渡す場合
// System.setIn(new FileInputStream("test.txt"))
Main.main(Array.empty)
//Main.scala
object Main extends App {

  // 一行取得
  val line = scala.io.StdIn.readLine
  println(line) // sample input

}

標準出力をオーバーライドする

System.setOutを使うことでJVM全体に影響を及ぼせる
以下コードは、全ての標準出力をdebug.logに書き込む

import java.io.PrintStream

val out: PrintStream = new PrintStream("debug.log")
System.setOut(out)

いやいやファイルに保存じゃなくて変数として取り出したいって時は

val outPutStream = new ByteArrayOutputStream()
val out: PrintStream = new PrintStream(outPutStream)
System.setOut(out)

//printlnは改行もあることを忘れずに
println("bamboo!")
print("tuna!")

val log = outPutStream.toString("utf8")
// log: String = "bamboo!\ntuna!"

//リセットしない限りprintの内容がoutPutStreamに追加される
outPutStream.reset()

val resetLog = outPutStream.toString("utf8")
// resetLog: String = ""

一つ不便なところがあって、標準出力をオーバーライドしちゃったせいで、
じゃぁoutPutStream.toString("utf8")で取り出した内容をみたいって時は、
まぁ当たり前だが普通にprintしても表示されない。

defaultをセットし直すか、外部ファイルに出力するしかない。

ちなみにdefaultに戻したいときは最初に保持して、それを後で代入すれば良さそう

val defaultSysOut = System.out
System.setOut(defaultSysOut)

余談

標準入出力をうまくテストできるライブラリとかないのかよ!
って探してたけど、そもそもScalaって言語で標準入力を使うって場面ってそうそうないじゃんって気づいた^^

もし便利なものがあったら教えてください!

コメント

タイトルとURLをコピーしました