Scalaでテスト実行前に任意の処理を実行する

こんにちは!まっちゃんです。

昔の話になりますが、
別プロジェクトの開発途中に、ローカル環境のデータベースだと思ってテストコードを実施したところ、
実は共通で参照しているテスト環境のデータベースに接続してしまい、
残念ながらテストデータがすべて消えてしまうという事が発生しました。
(本番環境だと非常によろしくないですね)

そのプロジェクトでは使用するデータベース全てのテストコード専用のものを用意し、
ローカル環境とテスト環境を完全に区切る対策を取りましたが、
自分のプロジェクトでは sbt を用いて対策していることを思い出したのでそれについて書かせていただきます。

上記、対策の手段として、次の事を行います。

  • テスト実行時、環境変数からDBの接続情報を取得する
  • DBの接続情報がローカルでない場合はテストを実施させない

今回使用しているプロジェクトはこちらの記事に上がっているものと、ほぼ同等のものです。

blog.engineer.adways.net

もしくはテンプレートをダウンロードを行い、各バージョンを修正して実行してみてください。

$ sbt new scala/scala-seed.g8

$ cd ${your_project}

build.sbt のバージョンを修正

import Dependencies._

lazy val root = (project in file(".")).
  settings(
    inThisBuild(List(
      organization := "com.example",
      scalaVersion := "2.11.8",
      version      := "0.1.0-SNAPSHOT"
    )),
    name := "Hello",
    libraryDependencies += scalaTest % Test
  )

project/build.properties のバージョン修正

sbt.version=0.13.13

テスト用の独自タスクを設定する

テスト実施は基本的にsbtを通して実行します。
テストに関するタスクを実装していきます。

build.sbt

lazy val preCheckTestTask = TaskKey[Unit]("preCheckTest", "preCheckTest")

preCheckTest という独自タスクを設定しました。

preCheckTest 独自タスクに処理を追加する

先ほど設定したタスクに処理を書いていきます。

build.sbt

preCheckTestTask := {
  // TODO 独自タスクの処理
}

ここに対策の手段である  

  • テスト実行時、環境変数からDBの接続情報を取得する
  • DBの接続情報がローカルでない場合はテストを実施させない

の処理を記述します。

build.sbt

preCheckTestTask := {
    if(sys.env.getOrElse("DB_HOST", default = "localhost") != "localhost") {
      sys.error("テスト実行不可")
    }
}

テスト実行前に、独自タスクの処理を実行させる

テストを実行するコマンドは複数あるので、
それぞれのコマンドごとに設定をします。

build.sbt

(test in Test) <<= (test in Test) dependsOn preCheckTestTask,
(testOnly in Test) <<= (testOnly in Test) dependsOn preCheckTestTask,
(testQuick in Test) <<= (testQuick in Test) dependsOn preCheckTestTask
build.sbt:17: warning: `<<=` operator is deprecated. Use `key := { x.value }` or `key ~= (old => { newValue })`.
See http://www.scala-sbt.org/0.13/docs/Migrating-from-sbt-012x.html
    (test in Test) <<= (test in Test) dependsOn preCheckTestTask /*,
                   ^

<<= は非推奨になった模様です。

sbt Reference Manual — Migrating from sbt 0.12.x

実行

実行するとこのような形になります。

$ sbt test

こちらは問題なくテストが実行されます。

$ DB_HOST=192.168.1.1 sbt test

...

[error] (*:preCheckTest) テスト実行不可

こちらはlocalhostではないのでテスト実行不可とエラーが出力されます。

テンプレートをダウンロードして確認する場合、
最終的な build.sbt は下記になります。

build.sbt

import Dependencies._

lazy val root = (project in file(".")).
  settings(
    inThisBuild(List(
      organization := "com.example",
      scalaVersion := "2.11.8",
      version      := "0.1.0-SNAPSHOT"
    )),
    name := "Hello",
    libraryDependencies += scalaTest % Test,
    preCheckTestTask := {
      if(sys.env.getOrElse("DB_HOST", default = "localhost") != "localhost") {
        sys.error("テスト実行不可")
      }
    },
    (test in Test) <<= (test in Test) dependsOn preCheckTestTask,
    (testOnly in Test) <<= (testOnly in Test) dependsOn preCheckTestTask,
    (testQuick in Test) <<= (testQuick in Test) dependsOn preCheckTestTask
  )

lazy val preCheckTestTask = TaskKey[Unit]("preCheckTest", "preCheckTest")

まとめ

今回は sbt の独自タスクを用いて対応できました。
上記の手段で、対策はできましたが完全ではありません。
設定ファイルの情報は参照していない、バージョンによる非推奨の部分があるなど、
不完全なものを対応していく場合、その点を考慮に入れながら開発・対策していかないといけません。

また冒頭でも書きましたが、別プロジェクトでは異なる対策を行っているので、
どの方法が適しているのかも考えないといけませんね。