IntellijIDEA + Scalaでテストのリモートデバッグ


探した感じ、巷に溢れている記事はsbtを通さないものしかなく、マルチプロジェクトだと動かなかったりするので汎用的にうごかせる設定(コマンドで立ち上げたsbtのtestに対してリモートデバッグする)をまとめました。

概要

Inttelijのリモートデバッガはシンプルな構成だとサクッと動くのですが、
マルチプロジェクトを使ってたり、テストフレームワークが混在していたり等々があると期待通り動かすのに一手間かかります。

runは問題ない

手元で動作確認をする時、runして状態を見たい際は以下のようなコマンドで実行してます。

sbt 'project $pjName' -jvm-debug $port -Dconfig.file=$file run

testの場合

sbt (test|testOnly)時は-jvm-debugだけではうまく動いてくれず、毎回以下の設定をtestタスクのjavaOptionに指定してdebugしたり

javaOptions in Test ++= Seq("-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9999")

IntellijIDEA上でテストにたいして一つ一つdebug confを作って、-Dconfig~などのテストに必要な設定を与えたりしていました。

これだと面倒なので、Intellij上とは別のteminalで立ち上げたsbt shellから実行したテストをデバッグできるようにしました。

これならtestOption等で指定したデフォルトオプションも読み込め、汎用的に使えて幸せ

jdwp

Java TM Debug Wire Protocol (JDWP) の略で、debuggerと、JVM との間の通信に使用するプロトコルです。詳しくは↓
https://docs.oracle.com/javase/jp/6/technotes/guides/jpda/jdwp-spec.html

以下2つの構成を取ることが出来ます。

  • JVM(sbt) = Server, Debugger(Intellij) = Client
  • JVM(sbt) = Client, Debugger(Inttelij) = Server

どっちでもよいのですが、JVMがサーバーのパターンだとDebugger起動忘れてsuspendしっぱなしが起こり得るのでDebugger側をサーバーにした手順を記します。(簡単に変えられるのでお好みでどうぞ)

debugタスク追加

↓のConfigの作り方を参考にうちのsbtにremote debugger用オプションを組み込んだConfigを追加しました。
https://www.scala-sbt.org/0.13/docs/Testing.html#Additional+test+configurations+with+shared+sources

ミニマムだと↓のような感じ

  lazy val debugTest = config("debug") extend Test

  lazy val root = (project in file("./"))
   .configs(debugTest)
   .settings(      
     fork in DebugTest := true,
     javaOptions in DebugTest ++= Seq("-agentlib:jdwp=transport=dt_socket,server=n,suspend=n,address=41230"
   )
   .settings(inConfig(DebugTest)(Defaults.testTasks))

※javaOption in Testでもいいんじゃないのと思うかもですが、CIのときにdebuggerがないと
JVMがClientの場合=コケる or JVMがServerの場合=debugger接続待ちのまま止まる)ので別Configを用意する必要がありました。

導入手順

  1. 右上のデバッグメニューから Edit Configurations をクリック

  2. 左上の+->remoteをクリック

  3. Name->好きな名前, Debugger Mode->Listen, Port->41230に設定し、OKをクリック

  4. 右上のデバッグメニューから作成した設定を選択し、🐞アイコンをクリック

  5. 緑色に点滅している状態で、breakpointを貼ってターミナルを立ち上げ、お好きにdebug:testなりdebug:testOnly *Hogehoge行う。
    ※1度の実行(debug:test等)が終わると、Debuggerが停止するので再度実行したい場合は4,5の手順を繰り返し行う

おまけ: JVMをサーバーにしたいときの注意点

Intellijのdebuggerは接続のタイミングがシビアなのか、debuggerが接続するまで待機するsuspend=yを指定しないとつながらなかったので注意