Testen, ob ActionListener aktiviert wird

Hallo zusammen,
Ich habe Blut geleckt am TDD und versuche, einige Tests für meine GUI zu schreiben. Folgendes:

Da ich eine etwas komplexere Struktur habe in der dynamisch JPanels mit Radio- und Checkboxen erzeugt werden, möchte ich testen, ob die Listener richtig zugewiesen werden. Dafür habe ich bisher folgenden Test:

@Test
	public void testIfRadioButtonListenersAreAssigned(){
		cPanel.buildView();
		cPanel.setRadioListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
                                                             //Hier soll der Test bestehen
				fail("ActionEvent startet");			}
		});
		
		JPanel viewPanel = (JPanel) cPanel.getView().getViewport().getView();
		
		cPanel.addCriterion(new Criterion("test"));
		((JRadioButton) viewPanel.getComponent(0)).doClick();
	}

Statt dem fail() bräuchte ich irgendwas, dass der Test dann und nur dann besteht, wenn diese Codezeile erreicht wird. Ich habe noch überlegt einfach eine boolean zu setzen, aber die müsste dann ja global sein. Das sollte ich doch eigentlich vermeiden oder?

Tut mir Leid wenn die Frage sehr trivial ist, aber TDD ist echtes Neuland für mich.
Grüße,
AceOfSpades

unabhängig von der Tauglichkeit als offizielles Testmittel:

du kannst vor die Methode ein lokales finales veränderliches Objekt, z.B. ein boolean-Array der Länge 1 erstellen,
auf Objekte finaler Variablen darf die Methode zugreifen und Wert im Array ändern

generell wuerde ich bei UI tests auf dafuer spezialisierte Libs ausweichen - zb http://docs.codehaus.org/display/FEST/Swing+Module.

Ansonsten wie Slater sagt… zb AtomicXXX klassen eignen sich auch dafuer

Für diesen speziellen Fall (Test, ob eine Methode aufgerufen wurde) und für weitere eignet sich Mocking recht gut. Am Beispiel Mockito würde der Testcode in etwa so aussehen (aus dem Gedächtnis getippt):

@Test
public void testIfRadioButtonListenersAreAssigned(){
  cPanel.buildView();
  // Hier wird ein mock angelegt, dass intern Methodenaufrufe mittrackt
  ActionListener mockActionListener = Mockito.mock(ActionListener.class);
  cPanel.setRadioListener(mockActionListener); 
  JPanel viewPanel = (JPanel) cPanel.getView().getViewport().getView();
  cPanel.addCriterion(new Criterion("test"));
  ((JRadioButton) viewPanel.getComponent(0)).doClick();
  // Hier wird das Mock befragt, ob die Methode genau einmal aufgerufen wurde.
  // Falls nicht schlägt der Test durch Werfen eines AssertionErrors fehl (wie bei JUnit-assertXXX)
  Mockito.verify(mockActionListener, Mockito.times(1)).actionPerformed(XXX);
}

Das XXX wäre noch zu füllen mit dem ActionEvent, dass beim Click gefeuert wird. An das kommt man evtl. nicht so leicht ran. Da mögen einem dann die bereits angesprochenen Swing-Testframeworks helfen.

Danke für die Tipps. Das FEST sieht sehr interessant aus, danke für den Link. Ich habe es jetzt erstmal mit einem boolean-Array gelöst, vielleicht fällt mir mit mehr Erfahrung noch was besseres ein:

@Test
	public void testIfRadioButtonListenersAreAssigned(){
		final ArrayList<Boolean> testPassed = new ArrayList<Boolean>();
		cPanel.setRadioListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				testPassed.add(((JRadioButton) e.getSource()).isSelected());
			}
		});
		
		JPanel viewPanel = (JPanel) cPanel.getView().getViewport().getView();
		
		cPanel.addCriterion(new Criterion("test"));
		((JRadioButton) viewPanel.getComponent(0)).doClick();
		
		assertTrue(testPassed.get(0));
		
	}

Damit Du hier nicht in die falsche Richtung abdriftest:
UnitTest testen **fachliche **Anforderungen an Deinen Code.
Das der ActionListener ausgelöst wird ist aber eine **technische **Anforderung. Das die erfüllt wird testen Modul- oder Anwendungs-Tests.

bye
TT

“boolean-Array” und dann eine ArrayList?
die ist auch noch im erhöhten Maße gefährlich: falls der Listener nicht ausgeführt wird bleibt die Liste leer -> nix mit get(0), jedenfalls nix gutes,
außerdem AutoBoxing

was ist überhaupt das ‘cPanel’, kann man den fraglichen RadioButton dort nicht mit einer normalen Methode holen?
((JRadioButton) viewPanel.getComponent(0)) und ((JRadioButton) e.getSource()) klingen ja abenteuerlich,
falls beides derselbe sein soll, dann ruhig nur einmal am Anfang in eine (wieder finale) Variable ablegen,

[QUOTE=Timothy_Truckle;98927]Damit Du hier nicht in die falsche Richtung abdriftest:
UnitTest testen **fachliche **Anforderungen an Deinen Code.
Das der ActionListener ausgelöst wird ist aber eine **technische **Anforderung. Das die erfüllt wird testen Modul- oder Anwendungs-Tests.

bye
TT[/QUOTE]

Hmm ok…Jetzt wo ich drüber nachdenke will ich ja eigentlich auch nur wissen, ob die Buttons einen Listener haben.
Da kann ich einfach schauen, ob mir getActionListeners() etwas zurück gibt. Wäre das dann im Sinne der UnitTests oder immer noch falsch?

[quote=TheAceOfSpades]Jetzt wo ich drüber nachdenke will ich ja eigentlich auch nur wissen, ob die Buttons einen Listener haben.[…]Wäre das dann im Sinne der UnitTests oder immer noch falsch?[/quote]Nicht falsch (weil testen sollte man sowas schon) aber eben kein Unittest, weil er keine fachlich begründete Anforderung testet, sonderen ein Implementierungsdetail. Wenn die Oberfläche in wenigen Jahren von Swing nach FX umgestellt wird hilft dieser Test nicht. Also sollte man wenigstens dafür sorgen, dass erkennbar wird, dass dies kein fachlich begründeter Test ist. Ich würde ihn in einem anderen Ordner ablegen (src/test/functional/gui ?).

Ich gebe zu, dass das jetz ziemlich akademisch wird, aber je näher man sich am Ideal bewegt um so weniger Probleme hat man später. Und eigentlich ist es genau dieses “später” wofür wir die UnitTests machen.

Ich denke am besten nähert man sich der Sache an, indem man sich an das Namenschema für Testmethoden von Roy Osherove hält:
public void getesteteMethode_Vorbedingung_erwatetesVerhalten()
Wobei sich Teil zwei und Teil drei aus den fachlichen Anforderungen herleiten lassen müssen.

Wenn man Schwierigkeiten hat den Namen so zu bilden kann der Test nicht “gut” werden…

bye
TT

Hm leute. Von tests und so lese ich hier immer oefter. Will jz keinen neuen thread aufmachen… aber das ist fuer mich komplett neuland. Was genau heisst das, testen? Ich mein, teste ich ob mein code klappt, oder wie?

Beispiel:
„will ich ja eigentlich auch nur wissen, ob die Buttons einen Listener haben.“

Hae, danm setz ich halt ein sysout(„setting listener“)
Vor button.setActionListener und wenns aufgerufen wird wars doch mit testen, oder wie ist das gemeint?
Kann mich jmd praezise aufklaeren? Danke! :smiley:

Mymaksimus du musst allerdings den code manuell aufrufen, manuell klicken und optisch pruefen ob alles korrekt ist. Es koennen durchaus mehrere klicks notwendig sein um den button erst zu erreichen etc.
Automatisierte tests werden geschrieben, gestartet und das endergebnis wird ueberprueft. Gruen oder Rot. Bei rot wird der code gefixt und der test gestartet bis wieder gruen ist. Wenn nach einem refactoring etwas rot ist, dann bekommt man dies viel schneller mit.
Statt einem sysout ginge auch ein breakpoint und debugger.
Zudem ist das loeschen von sysouts ein aendern des codes der die gesamte integritaet der software beeinflusst.

[QUOTE=mymaksimus]Hm leute. Von tests und so lese ich hier immer oefter. Will jz keinen neuen thread aufmachen… aber das ist fuer mich komplett neuland. Was genau heisst das, testen? Ich mein, teste ich ob mein code klappt, oder wie?
[/QUOTE]
Lies mal hier: http://tutorials.jenkov.com/java-unit-testing/simple-test.html

Man will gerade keine sysouts oder breakpoints oder //TODO oder sonstwas in den Sourcen haben…

[quote=mymaksimus]Kann mich jmd praezise aufklaeren? Danke![/quote]Wir reden von automatisierten Tests.
Um Software zu testen benötigt man wissen: Man muss wissen wie die Software benutzt wird und welche Ausgabe für bestimmte Eingaben erwartet wird (oder kurz: wann ist der Test erfüllt).

Bei der von Dir vorgeschlagenen Methode muss derjenige, der den Test durchführt dieses Wissen haben, weil die (TestLog-)Ausgabe sonst keinerlei Aussagekraft für ihn hat. Wenn Du eine komplette Software auf diese Weise testen willst must Du dieses Wissen für jede der Test-Ausgaben haben. Die Lösung dafür sind oft Testprotokolle, die ein Tester abarbeiten und die formulierten Ergebnisse mit den tatsächlichen Ausgaben der Software vergleichen muss. Davon abgesehen, dass dieses Verfahren für den Tester eine mentale Herausforderung ist besteht die Möglichkeit, dass die Vorgaben ungenau beschrieben sind (weil von einem Entwickler mit implizitem Wissen gemacht) und der Tester (der **anderes **implizites Wissen hat) sie falsch interpretieren könnte.

Bei automatisierten Tests liegt dieses Wissen in den Tests. Das bedeutet, das jeder diese Tests ausführen und damit eine Aussage über die Funktionsfähigkeit der Software machen kann, auch wenn er keine einzige Zeile des Codes kennt.
OK, das setzt natürlich ein gewisses Maß an Vertrauen darauf voraus, dass die Tests sinvoll implementiert sind. Aber letztendlich muss man auch bei der manuelle Testmethode dem Tester vertrauen, dass er seine Aufgabe gewissenhaft und korrekt erledigt.

Für uns Entwickler ist die wichtigste Teststufe der Unittest. Dabei wird ein sehr kleiner Teil der Software isoliert auf eine aus den Anforderungen abgeleitete Annahme über die Unit (welche Ausgabe erzeugt sie bei einer bestimmten Eingabe) geprüft. Ein Progamm mitterer Komplexität bringt es schnell auf mehrere Tausend solcher Unittests. So viele Tests manuell zB. durch Analyse von Logs oder anhand eines Testprotokolls machen zu wollen ist praktisch unmöglich…

Ein Vorgehen bei der Erstellung solcher Tests ist, diese vor dem zu testenden Code zu schreiben.
die wichtigsten Vorteile sind:
[ul]
[li]Vergessen oder Vernachlässigen der Tests nicht möglich -> Hohe Testabdeckung
[/li][li]man sieht, dass Änderung am Produktiv-Code das Testergebnis ändert-> Test ist verlässlich
[/li][/ul]

Viel Spass beim Weiterlesen…

bye
TT

PS: würde bitte ein Moderator den Thread bei Post #10 Teilen? (und dann diese Frage löschen…)