プログラムを作ったあと、通常は通らないルートをテストしたいとき、皆さんはどうしていますか?
一般的には以下のような形で実行すると思います。
- 特定の箇所にブレークポイントを設定する
- テストしたいアクションを実行する
- ブレークポイントに止まるのを待つ
- ブレークポイントに止まったら、手動で値を変更する
でも、値を変更した状態を何度か操作を繰り返したいときってありまして、こういう場合は値が自動で書き換わってほしいと思うわけなんですよ。
思ったことありませんか?
実はこれは条件付きブレークポイントを使用することで実現できますが、ベテラン勢でも割と知らない人も多いので残しておきます。
前提条件と予備知識
サンプルコードと想定パターン
RestAPIを実行して、状態チェック→パース→モデル変換を行うメソッド。(内容は特に重要ではありません)
public JmaFeed getFeed() {
ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
if (!response.hasBody()) {
return null;
}
String responseBody = response.getBody();
if (isNull(responseBody)) {
return null;
}
JmaXmlFeed xml = xmlParser.read(response.getBody(), JmaXmlFeed.class);
if (isNull(xml.getTitle())) {
return null;
}
return modelMapper.map(xml, JmaFeed.class);
}
このメソッドで、普段は起こり得ない
isNull(responseBody)
isNull(xml.getTitle())
となる条件を何度も確認したいと仮定します。
なお、xml.getTitle()
は xml.setTitle()
で値を書き換え可能、ソースコードは変更しないという条件も加えておきます。
予備知識:条件付きブレークポイント
IDEでデバッグする際、ブレークポイントには条件を設定することが可能です。
これは条件付きブレークポイントと呼ばれたりもします。
■IntelliJのブレークポイントで右クリックしたときの画面
この条件の普通の使い方は、「変数がAが0の場合」とか「変数Bがnullの場合」とかを設定します。
つまり、以下のような書き方をします。
■例: 変数 responseBodyがnullの場合にブレークする
これで responseBody = null
のときにブレークポイントで止めることができます。
デバッグ時にソース変更もブレークもせずに変数の値を変更する方法
ここからが本題です。
2つの方法を説明していきます。
条件付きブレークポイントで変数値に代入をする
条件付きブレークポイントの条件は最終的にtrue/falseを返せば良いのであって、途中は何をしてもOK。
なので、こういう条件を設定することも可能。
(responseBody = null) != null
この式は見ての通り
- responseBodyにnullを代入
- responseBodyがnullでないことを判定
を行うので、条件は必ずfalseになりブレークすることはありません。
つまり、ブレークポイントで止まることなく、自動でresponseBodyを書き換えることができたというわけです。
条件付きブレークポイントでsetterを呼び出す
次に2.の条件 isNull(xml.getTitle()))
を満たす方法ですが、さっきよりも工夫が必要です。
というのも、今回のプログラムでは xml.setTitle(null)
で値を変更できますが、setterの多くはvoidメソッドです。
ということは、メソッド結果を何かと比較することができず、xml.setTitle(null) != null
のような式は条件に設定できません。
したがって、以下のような式にしてみます。
Optional.of("a").map(x -> { xml.setTitle(null); return false; }).get()
xml.setTitle(null)
を実行をしつつ、falseを返す方法として、メソッドを引数にできる Optional#map
を使って実現しました。
ラムダ式を引数に持って任意の値を返すことができれば、他の方法でも実現できると思います。
ちなみに、Eclipseの場合は xml.setTitile(null)
を設定するだけでも期待通りに値を書き換えてくれます。
IDEによって条件に差があるようですので、自分で使用されているIDEの挙動を確認してみてください。
まとめ
デバッグ時にブレークせずに変数の値を変更する方法のまとめです。
1. 条件付きブレークポイントで変数値に代入をする
代入をして、falseとなる条件を書く
(responseBody = null) != null
2. 条件付きブレークポイントでsetterを呼び出す
Optional#map でsetterを呼び出してfalseを返す
Optional.of("a").map(x -> { xml.setTitle(null); return false; }).get()
他の言語でも同様の仕組みを用いれば、同じことはできると思います。