Randoop – automatische Testgenerierung und mehr


Quelle : http://code.google.com/p/randoop

Wie bereits erwähnt, habe ich mir das Thema „dynamische Programm-Analyse“ mal ein wenig genauer angeschaut. Dabei ist mir das Werkzeug Randoop in die Hände gefallen. Und seit letztem November gibt es dafür auch ein hübsches Eclipse-Plugin, welches bequem per Update-Url installiert werden kann. Bei meinem Eclipse-Helios funktionierte das wunderbar und unter „Show View“ gab es dann auch die versprochene Randoop-View. Das ganze wollte ich dann auch mal testen und habe mir ein RandoopTestProject angelegt mit folgender Struktur :

null

package core;

import java.io.File;

public class Util {

	/**
	 * just an integer
	 */
	private int number;
	
	/**
	 * the constructor.
	 */
	public Util(int number) {
		this.number = number;
	}

	/**
	 * a senseless method.
	 * @param filename just a String
	 */
	public String methodXY(String filename){		
		File file = null;
		filename = "Z:\\blubb\\" + filename;
		return file.getAbsolutePath().toString();
	}

	/**
	 * @return the a
	 */
	public int getNumber() {
		return number;
	}

	/**
	 * @param a the a to set
	 */
	public void setNumber(int number) {
		this.number = number;
	}	
}

Dazu gibt es eine Helper-Klasse, in der wir ein paar zusätzliche Inputs für die Test-Generierung festhalten :

package core;

import randoop.TestValue;

/**
 * additional values for randoop test-generation
 * 
 * @author Christoph
 *
 */
public class TestValuesHelper {

	    @TestValue
	    public static int a1 = 0;	    
	    @TestValue
	    public static int a2 = -23;
	    @TestValue
	    public static int a3 = 42;	    
	    @TestValue	    
	    public static int a4 = 13;	    
	    @TestValue
	    public static int a5 = 26;	    
	    @TestValue
	    public static int a6 = 312;	    
	    @TestValue
	    public static int a7 = 90;	    
	    @TestValue
	    public static int a8 = -513;	    
	    @TestValue
	    public static int a9 = 10;

	    @TestValue
	    public static int[] arr1 = {1,2,3,4,5,6,7,8};
	    @TestValue
	    public static int[] arr2 = {34,12,66,23344,121};
	    
	    @TestValue
	    public static String s1 = "spongebob";
	    @TestValue
	    public static String s2 = "blubb";
}

Das Randoop-Plugin wird mit einer eigenen Run-Configuration ausgestattet, die genauen Parameter lassen sich gut auf der Doku-Seite nachlesen. Für den Testlauf habe ich folgende Konfiguration aufgestellt :

null

null

1000 Tests sollten erstmal reichen, um das auszulösen, was ich eigentlich bezwecke : das Auffinden einer NullPointerException im Code. Die Methode methodXY(String filename) ist ja geradezu prädesteniert dafür. Außerdem sollten 1000 Zufallstests auch ausreichen, um eine 100%ige Testabdeckung zu erreichen. Die TestValuesHelper-Klasse sollte beim Erzeugen der Randoop-Tests mit in der Auswahl der zu testenden Objekte stehen, dann weist eine entsprechende Meldung in der Eclipse-Konsole auch darauf hin :

ANNOTATION: Detected @TestValue-annotated field int "a1" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field int "a2" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field int "a3" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field int "a4" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field int "a5" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field int "a6" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field int "a7" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field int "a8" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field int "a9" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field int[] "arr1" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field int[] "arr2" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field java.lang.String "s1" in class core.TestValuesHelper. Will collect its primtive values to use in generation.
ANNOTATION: Detected @TestValue-annotated field java.lang.String "s2" in class core.TestValuesHelper. Will collect its primtive values to use in generation.

Und in der Randoop-Console sieht man auch das erstrebte Ergebnis :

null

In der Projekt-Struktur sieht man nun auch die von Randoop erzeugten Tests. Darin sind sowohl die Regressions-, als auch die Failure-Tests enthalten :

null

Der Failure-Test bringt dann auch genau den Ort und die Ausprägung des Fehlers ans Licht :

null

Und nach einem Coverage-Run über EclEmma hat man auch ohne viel Aufregung 82,5% Test-Abdeckung :

null

Na also, der erste Test lief ja mal (fast) problemlos…