AgileUnit: Ein agiler Ansatz

Wo implementiere ich den Test?

Führen wir das Beispiel der Klasse Sum.java weiter. AgileUnit erzeugt automatisch beim Erstellen der Klasse eine Testklasse namens AgileUnitSum.java und eine Systemklasse namens TestSum.java. TestSum.java ist eine vom System generierte Hilfsklasse und muss nicht geändert werden. Das ist die Klasse für den Aufruf der lokalen Tests während der Entwicklung bzw. die Klasse die vom Framework später aufgerufen wird, wenn es Regressionstests durchführen möchte (wenn der Eintrag im xml-File vorhanden und diese Option aktiv ist – siehe Listing 3-4). In der Klasse AgileUnitSum.java hingegen definiert man die eigenen Tests nach Belieben.

Listing 4 – continuous.xml

ch.algoritmo.framework.agileunit.test.example.TestSum

AgileUnit durchläuft Regressionstests nur, wenn die Property skip.continuous.stop auf true gesetzt ist. Das hat zur Folge, dass der Regressionstest stoppt und eine Exception geworfen wird, wenn ein Test durchfällt. Daher der Name skip.continuous.stop. Diese Property soll vor allem während der Entwicklung auf true gesetzt sein, damit diese Tests kontinuierlich durchlaufen werden.

Im Buildsystem gibt man die Klasse an, welche die Regressionssuite anstößt. Sie heißt RunContinuousTestFromXmlFile.java. Vor der Auslieferung setzt man diese Property kundenseitig auf false, sodass die Tests lazy und nur auf die wirkliche User-Interaktion reagieren. Das heißt, dass ein Test ab sofort nur dann durchlaufen wird, wenn der Benutzer tatsächlich interagiert (z.B. mit der GUI) und Werte verarbeitet werden (siehe Listing 3 – agileunit.properties). Listing 5 zeigt beispielhaft auf, wie man User-Interaktion definiert und Business Rules deklariert. Siehe Referenzen 1-6 für Details.

Listing 5 – AgileUnitSum.java
public final class AgileUnitSum {
	private AgileUnitSum() {
		super();
	}
// Referenz 1 #########################################
	public static final void simulateUserInteraction( final Sum sum , final TestProperties rule ) {
		// PLUS USER INTERACTION SIMULATION 1
		String userInputValue1a = rule.get( "user.input.value.1a" );
		String userInputValue1b = rule.get( "user.input.value.1b" );
		// USER INPUT VALUE MUST MATCH THE METHOD VALUE TYPE!
		sum.plus( Integer.valueOf( userInputValue1a ) , Integer.valueOf( userInputValue1b ) );
		// PLUS USER INTERACTION SIMULATION 2
		String userInputValue1c = rule.get( "user.input.value.1c" );
		String userInputValue1d = rule.get( "user.input.value.1d" );
		// USER INPUT VALUE MUST MATCH THE METHOD VALUE TYPE!
		sum.plus( Integer.valueOf( userInputValue1c ) , Integer.valueOf( userInputValue1d ) );
		// MINUS USER INTERACTION SIMULATION 1
		String userInputValue2a = rule.get( "user.input.value.2a" );
		String userInputValue2b = rule.get( "user.input.value.2b" );
		// USER INPUT VALUE MUST MATCH THE METHOD VALUE TYPE!
		sum.minus( Integer.valueOf( userInputValue2a ) , Integer.valueOf( userInputValue2b ) );
	}
// Referenz 2 #########################################
	
	public static final void applyBusinessRule(
			final TestSum test ,
			final TestProperties rule ,
			final Object input ) {
		final Method m = AgileUnit.clazz( test.getClazz() );
		final Output o = m.method( test.getMethod() );
		final Shall aUnit = o.output( test.getInput() );
		test( test , rule , aUnit );
	}
// Referenz 3 #########################################
	
	private static final void test( final TestSum test , final TestProperties rule , final Shall aUnit ) {
		// place your test methods here - see examples
		testPlus( test , rule , aUnit );
		testMinus( test , rule , aUnit );
	}
	
	// change the name of this method to the test name you want
	// create snippet for this methods your use our own snippet
// Referenz 4 #########################################

	private static final void testPlus( final TestSum test , final TestProperties rule , final Shall aUnit ) {
		Be comparation;
		// TEST: int plus(int a, int b)
		if (test.getMethod().equals( rule.get( "user.method.name.1a" ) )) {
			comparation = Be.valueOf( rule.get( "comparation.value.1a" ) );
			String expectationValue1a = rule.get( "expectation.value.1a" );
			// USER INPUT VALUE MUST MATCH THE METHOD VALUE TYPE!
// Referenz 5 #########################################

			Integer expectation = Integer.valueOf( expectationValue1a );
			aUnit.shall( comparation ).as( expectation );
		}
	}
	
	// change the name of this method to the test name you want
	private static final void testMinus( final TestSum test , final TestProperties rule , final Shall aUnit ) {
		Be comparation;
		// TEST: int minus(int a, int b)
// Referenz 6 #########################################

		if (test.getMethod().equals( rule.get( "ignore" ) )) {
			comparation = Be.valueOf( rule.get( "comparation.value.1a" ) );
			// ignored method test
		}
	}
1. Methode simulateUserInteraction()

Vieles bereitet das Plug-in vor, sodass wir uns nur um den eigentlichen Test kümmern müssen. simulateUserInteraction() ist für die Regressionssuite gedacht. Ist die Property aktiv, also auf true gesetzt worden, würde diese Methode vom Framework aufgerufen werden, wenn die Klasse RunContinuousTestFromXml-File.java im Buildsystem angestoßen wird.

2. Methode applyBusinessRule()

Vom Plug-in vorbereitet. Bedarf keinerlei Änderung. Wird vom Framework aufgerufen.

3. Methode test()

Wird vom Plug-in zwar vorbereitet, ist aber anfänglich leer. Hier platziert man die eigenen privaten Testmethoden wie links unten dargestellt (testPlus(), testMinus()).

4. Show-Case-Methode testPlus()

Hier wird klar, dass alles konfigurierbar ist. Alle Werte (Methodenname, Vergleichswerte und Erwartungswerte) erhält man von einer Property-Datei (siehe Listing 2 – TestSum.properties). Das wichtigste hier ist das If-Statement und die Erwartungswerte. Auf diese zwei Punkte soll hier genauer eingegangen werden.

5. Expectation

Die Definition von expectation muss mit dem erwarteten Methodentyp übereinstimmen, da wir Werte von einer Property-Datei einlesen und dafür sorgen müssen, dass die Typen stimmig sind. Deshalb steht ein Kommentar oberhalb der Definition von:

Integer expectation = Integer.valueOf (expectationValue1a)
6. Ignore

Das If-Statement benötigt man, um die Methode ignorieren zu können, wie im Fall von testMinus dargestellt.

Wo finde ich AgileUnit? Das Tool steht jedem kostenlos als Source-Forge-Projekt zur Verfügung.

Ricardo Ferreira ist Dipl. Applikationsentwickler HF-NDS / Dipl. Techniker HF Elektrotechnik Fachrichtung technische Informatik. Er hat bei der Abraxas AG in Zürich als Test Engineer angefangen und wechselte später zum Client Competence Center-Team. Zur Zeit arbeitet er an Client Lösungen auf Eclipse RCP Basis im agilen Umfeld. Davor war er in der industriellen Automatisierung als Projektleiter tätig. Kontakt: ricardo.ferreira[at]abraxas.ch
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.