概要
Randoopというツールをご存知でしょうか?
簡単に言うとJUnitでカバレッジの高いテストコードを自動で生成してくれるツールです。
Javaで動くためGradleタスクとして組み込むこともできます。
そもそもテストコードとは?
テストコードは主にレグレッションテストで用いられるもので、その名の通りコードでテストを書くものです。
手で単体テストを実施するのではなく、メソッド単位で入力値と想定される出力値を書くことで、コード修正後に想定外の出力になることを検知することができます。
テストコードの課題
テストコードを作成すること自体は良いことなのですが、現実問題としてテストコードの作成にはその処理を書くよりも工数が掛かります。
最近はAIに任せるなど聞きますが、現場によってはセキュリティ面で使用できないこともあります。
それに良いテストコードを書くには要件を明確、かつ正確に把握しそれを元に過不足のないコードを作成する必要があります。
AI側にそれをうまく伝えるには伝える側のスキルが必要で、人間間のやり取り以上に詳細に伝えないとAIも正確性の高いテストコードを提供できません。
テストコードはそういう意味で現在もハードルが高く、打鍵テストに寄る現場が多いと感じております。
もちろんメリットもあり、一度作成したらレグレッションテストが楽になります。楽になると後続の開発時にテスト工数をある程度減らすことができます。
まあお客さんからしたら予算もあるので、最初の開発時に「テストコード作りましょう」とか言っても、受け入れられないことの方が多いと思いますが。。。
Randoopとは
業務コードを静的解析ツールで分析し、引数の値にいろんな値を設定するテストコードを作成してくれるツールになります。
何でもOpenJDKのTreeSetで不具合を検知した実績があるそうな。
[原文]
A TreeSet is an ordered collection. According to the API documentation, this constructor call should throw a ClassCastException because the list element is not Comparable. But the constructor silently (and problematically) accepts the list.
[DeepL翻訳]
TreeSetは順序付けられたコレクションである。APIのドキュメントによると、このコンストラクタ呼び出しは、リスト要素がComparableではないため、ClassCastExceptionをスローするはずである。しかし、コンストラクタは黙って(そして問題なく)リストを受け入れる。
[ページ]
https://randoop.github.io/randoop/manual/index.html
ただのドキュメントのミスじゃね?という発言は置いておいて。。。 自動生成したコードはCastの挙動確認にも対応していそうですね。
なお作成されるテストコードは以下の2種類らしいです。
- 実行時に成功するテスト。レグレッションテスト観点。
- 実行時に失敗するテスト。メソッドに潜在バグがないか確認する観点。
おおよそテストコードを作成する時と同じ観点で作成されていますね。
成功するテストは当たり前で、失敗するテストも引数によっては予期せぬエラーが発生するケースを検知するのに使用する、という感じでしょうか。
ぱっと見、記載内容は良さげに見えます(謎上目線)。
使用方法
方法が載っているページはこちらです。
コンソール上でJavaで動くそうな。Gradleでも実行できそうで嬉しいですね。
1. クラスを掲載したファイルを作成
myClasses.txt
というものを作成するそうです。
マニュアルでは作成したものがこちらになります。
java.util.Collections
java.util.TreeSet
どうやらビルドパスを記載するようですね。
2. javaでRandoopを実行
公式からRandoopのjarファイルをダウンロードして以下のように実行するそう。
$ java -classpath ${RANDOOP_JAR} randoop.main.Main gentests --classlist=myclasses.txt --time-limit=60
実行メッセージは以下のとおりです。
Created file: my/home/directory/RegressionTest0.java
Created file: my/home/directory/RegressionTest.java
Created file: my/home/directory/ErrorTest0.java
Created file: my/home/directory/ErrorTest.java
done.
なお、生成したテストコードの依存ライブラリはjunit.jarとhamcrest-core.jarの2つらしいです。
なので生成したコードを動かす時は上記2つのビルドパスを通してから実行してください。
(オマケ)Gradleに組み込んでみる
GradleにRandoopのタスクを組み込むと以下のようになります。
// jarファイルのパスを通す
dependencies {
implementation(
files('./lib/randoop-all-4.3.2.jar')
)
}
// タスク登録
task generateRandoopTest(type: JavaExec) {
description = 'Generate Class Test Codes'
classpath = sourceSets.main.runtimeClasspath
mainClass = "randoop.main.Main"
args = [
'gentests',
'--classlist=./service.txt',
'--junit-output-dir=src/test/java',
'--time-limit=60',
'--output-limit=20000',
'--junit-package-name=com.example.service.implement'
]
}
これで実行する時もGradleだけ共有できれば誰でも実行できますね!
まとめ
- Randoopは実装工数を削減できる
- 静的解析ツールでレグレッションテストや潜在バグを検出するテストを作成できる
「序」ということで次は実際にテストコードを作成してみようかと思います。
個人的にはEnumのバリエーションテストやNULL、空文字とかそこらへんをテストしてくれるとめちゃんこ嬉しいのですが。。。
追って報告します(望み薄)!