Java
JUnit
Randoop

Java用JUnitテストクラスジェネレータRandoopについて調べてみた

概要

Randoopは、Java用テストクラスジェネレータ、指定したクラスのJUnit形式のテストクラスを自動生成する。

特徴

ソースから、リグレッションテストと、エラー検知テストを生成する。

Randoop generates unit tests using feedback-directed random test generation

フィードバック指向のランダムテスト生成だそうです。
OpenJDKのバグを見つけた実績があるそうです。
https://randoop.github.io/randoop/manual/index.html#Introduction

ユースケース、業務のどこに使えるか(いつ使うか、どのように使うか)

”自前で作るテストケース以外の方法で”デグレを検知できる。

注意点、リスク

コードの改修に合わせて、Randoopのテストもメンテナンス(自動生成しなおし)する必要がある。
導入するなら、ある程度自動化が進んでいるプロジェクトの方が良さそう。

使い方

(参考)GitHubプロジェクト

https://github.com/taka2/randoop-sample

SumServiceを利用して、MultiplyServiceを実装しています。
(MultiplyServiceは、SumServiceに依存)

SumService、MultiplyServiceのソース
https://github.com/taka2/randoop-sample/tree/master/src/main/java/randoop_sample

1. テスト生成対象クラスをmyclasses.txtに書く

randoop_sample.SumService
randoop_sample.MultiplyService

https://github.com/taka2/randoop-sample/blob/master/myclasses.txt

2. Randoopでテストクラスを生成する

java -classpath $(RANDOOP_JAR) randoop.main.Main gentests --classlist=myclasses.txt --timelimit=60

生成されたクラス
https://github.com/taka2/randoop-sample/tree/master/src/test/java

3. 生成したクラスをそのまま実行する

これは当然テストが全ケースパスする。

4. SumServiceを修正する

return a+b;

return a+b+1;

にする。
939/946がfail。SumServiceもMultiplyServiceもデグレしているので、ほとんど全てのテストがfailする。

5. MultiplyServiceを修正する

SumServiceを元に戻して、MultiplyServiceを修正する。

for(int i=0; i<b; i++) {

for(int i=0; i<=b; i++) {

にする。

482/946がfail。MultiplyServiceのみデグレしているので、半分くらいのテストがfailする。

結論

繰り返しになりますが、メリットでデメリットはこんな感じかと。

メリット

”自前で作るテストケース以外の方法で”デグレを検知できる。

デメリット

コードの改修に合わせて、Randoopのテストもメンテナンス(自動生成しなおし)する必要がある。

そもそも、ErrorTestが生成されなかったり(エラーがないから?)、RegressionTestも生成されないケースがあったり、結構不安定なので、いまいち未完成なのかなと思ったり。実戦投入は厳しいかなと思う。。

java.util.TreeSetのテストクラス生成に失敗したケース。

Instantiation error for operation java.util.TreeSet. : () -> java.util.TreeSet
Instantiation error for operation java.util.TreeSet. : () -> java.util.TreeSet
Instantiation error for operation java.util.TreeSet. : () -> java.util.TreeSet
Instantiation error for operation java.util.TreeSet. : () -> java.util.TreeSet
Instantiation error for operation java.util.TreeSet. : () -> java.util.TreeSet
Instantiation error for operation java.util.TreeSet. : () -> java.util.TreeSet
Instantiation error for operation java.util.TreeSet. : (java.util.Collection<? extends E>) -> java.util.TreeSet
Instantiation error for operation java.util.TreeSet. : (java.util.Collection<? extends E>) -> java.util.TreeSet
Instantiation error for operation java.util.TreeSet. : (java.util.Collection<? extends E>) -> java.util.TreeSet
Instantiation error for operation java.util.TreeSet. : (java.util.Collection<? extends E>) -> java.util.TreeSet
Instantiation error for operation java.util.TreeSet. : () -> java.util.TreeSet
...

参考資料