sbtでHTTP接続テスト用のMockServerを立てる

## 経緯

[_Scaladia_](https://github.com/giiita/scaladia) というDIフレームワークを作っています。

この中に、_akka http_を適当にラップしたモジュールがあるんですが、
_HTTPクライアント_を作る上で、そのクライアントを通してちゃんとHTTP通信できるのかをテストしなきゃいけません。

そんなにまだ開発の進んでいないmoduleだったので今までは暫定的によくないこととしりつつ、ネタ要素の強い適当なAPIを使っていました。
もちろん、テスト回した時に数回叩くくらいだったので、迷惑はかからないだろうという前提です。

でもやっぱり外部リソースに依存するテストを書いてしまうとネットワーク環境がないと動かないテストになってしまうので、環境問わず動くテストを用意しようということで、今回は `sbt test` の際にHTTP系モジュールだけLocalのHTTP Serverにリクエストさせるようにしました。

意外と類似情報がなかったので、HTTP関係のOSSとかどうしてるんだろうなと思いつつ、ひとまず実現できたので記録しようと思います。

## test classpath内でHTTPサーバーを立てればいいのでは？

それも考えたのですが、このフレームワークではAkka HTTPを使用しており、HTTPサーバーを起動するに当たって、
`application.conf` に

```scala 
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 で定義します。

```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` が

```json
{
  "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を起動する

```bash
#!/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を終了するスクリプト

```bash
#!/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の事例の紹介なので、設定まるごと載せてしまいます。

```scala
  .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` が結構便利ですよ、というエントリでした。