経緯
Scaladia というDIフレームワークを作っています。
この中に、_akka http_を適当にラップしたモジュールがあるんですが、
_HTTPクライアント_を作る上で、そのクライアントを通してちゃんとHTTP通信できるのかをテストしなきゃいけません。
そんなにまだ開発の進んでいないmoduleだったので今までは暫定的によくないこととしりつつ、ネタ要素の強い適当なAPIを使っていました。
もちろん、テスト回した時に数回叩くくらいだったので、迷惑はかからないだろうという前提です。
でもやっぱり外部リソースに依存するテストを書いてしまうとネットワーク環境がないと動かないテストになってしまうので、環境問わず動くテストを用意しようということで、今回は sbt test の際にHTTP系モジュールだけLocalのHTTP Serverにリクエストさせるようにしました。
意外と類似情報がなかったので、HTTP関係のOSSとかどうしてるんだろうなと思いつつ、ひとまず実現できたので記録しようと思います。
test classpath内でHTTPサーバーを立てればいいのでは?
それも考えたのですが、このフレームワークではAkka HTTPを使用しており、HTTPサーバーを起動するに当たって、
application.conf に
akka {
~~
}
的なのを用意しないとActorSystemのインスタンスを作れなくてエラーになります。
んーテストとはいえ、もうちょっと美しい感じでできないかな・・・と思い、その解法は使いませんでした。
単純に src/test/resources にtest用のconfを置くのはダサい気がしたというだけですね。
そして、ScalaでHTTP Serverを立てる場合、classのライフサイクルを意識して、TEST中に一回だけしか起動しないように注意する必要があるので、ちょっとシンプルさにかけるかなと思いました。
かといって、test時だけFinatraなど他のHTTPサーバーを立てるのなんて論外なので、採用しませんでした。
どうしたか
http関連モジュールのテスト時のみ、npmでlocalにjson-serverを立ち上げました。
npmに、 json-server という、なかなか便利なmoduleがあるんですよね。
json-server --watch [jsonFile] とするだけで、endpointとresponseを作ってくれます。
まずは endpoint と response を json で定義します。
{
"endpoint": {
"status": "success",
"value": {
"id": 90,
"joke": "Chuck Norris always knows the EXACT location of Carmen SanDiego.",
"categories": []
}
}
}
これを json-serverに渡すと、 http://localhost:3000/endpoint が
{
"status": "success",
"value": {
"id": 90,
"joke": "Chuck Norris always knows the EXACT location of Carmen SanDiego.",
"categories": []
}
}
を返すAPIになります。
続いて、 json-serverを起動するshellです。
- なかったらnvmをinstall
- なかったらnpmをinstall
- なかったらjson-serverをinstall
- json-serverを起動する
# !/bin/bash
script=$(cd $(dirname $0); pwd)
install_nvm() {
if ! test -s $HOME/.nvm; then
echo "NVM installing..."
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
fi
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
echo "NVM: `nvm --version`"
}
install_npm() {
npm_exist=`npm --version; echo $?`
if test $npm_exist -ne 0; then
echo "NPM installing..."
nvm install stable
fi
echo "NPM: `npm --version`"
}
install_json_server() {
if test `npm ls -g --depth=0 | grep json-server | wc -l` -eq 0; then
echo "JSON-SERVER installing"
npm install -g json-server
fi
}
run_jsonserver() {
json-server --watch ${script}/mock-response.json &
sleep 2
}
install_nvm
install_npm
install_json_server
run_jsonserver
で json-serverを終了するスクリプト
# !/bin/bash
if test `ps -ef | grep "json-server --watch" | wc -l` -ne 0; then
ps -ef | grep "json-server --watch" | grep -v "grep" | awk '{print $2}' | xargs kill
fi
これで必要なものは揃ったので、test時にこいつらを叩かせます。
今回はOSSの事例の紹介なので、設定まるごと載せてしまいます。
.settings(
name := "scaladia-http",
description := "Http client for Scala.",
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-http" % "10.1.8",
"com.typesafe.akka" %% "akka-actor" % "2.5.23",
"com.typesafe.akka" %% "akka-stream" % "2.5.23",
"com.typesafe.akka" %% "akka-http-jackson" % "10.1.8",
"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.9.9"
),
unmanagedClasspath in Test ++= (unmanagedResources in Compile).value,
testOptions in Test ++= Seq(
Tests.Setup { _ =>
import scala.sys.process._
Process("sh sh/setup-testing-http-server.sh").run
},
Tests.Cleanup { _ =>
import scala.sys.process._
Process("sh sh/shutdown-testing-http-server.sh").run
}
)
)
Tests.Setup でテスト前にserverを起動
Tests.Cleanup で終了時にserverをkill
という感じです。
どっちが美しいかと言われると、人の好み感が強いですが、
個人的に、classのライフサイクルを意識する必要のない作りなので、こっちの方がシンプルでいいかなぁと思っています。
実装はちょっと複雑になっていますが。。。
testOptions in Test が結構便利ですよ、というエントリでした。