6. Test von Software mit Nutzungsoberflächen Test von...
Transcript of 6. Test von Software mit Nutzungsoberflächen Test von...
Stephan Kleuker 194Software-Qualität
6. Test von Software mit Nutzungsoberflächen
• Ansätze zum Oberflächentest
• Erinnerung: GUI-Aufbau mit Swing
• Einführung in FEST
• Systematische Nutzung von FEST
• Teststrategien
• Capture & Replay
• Vorstellung von Marathon
• Kurzvorstellung Selenium
Stephan Kleuker 195Software-Qualität
Test von Oberflächen
• Grundsätzlich sind Oberflächen gewöhnliche SW-Module und können wie diese getestet werden
• Variante 1: Direkte Ausführung von GUI-Aktionen über Testsoftware
• Allerdings: Für xUnit-Werkzeuge ist der Zugriff auf Oberflächen teilweise schwierig (xUnit muss auf Oberflächenkomponenten zugreifen und diese bedienen können, JButton in Java hat z.B. Methode doClick())
• folgende Folien: FEST (Fixtures for Easy Software Testing, http://fest.easytesting.org/), hier GUI-Anteil
• Variante 2: Das Werkzeug zeichnet alle Mausbewegungen und Tastatureingaben auf; diese können dann zur Testwiederholung erneut abgespielt werden (später)
• Werkzeugalternative: Google Web Toolkit (Window Tester) http://code.google.com/intl/en/javadevtools/wintester/html/index.html
Stephan Kleuker 196Software-Qualität
Erinnerung: GUI-Aufbau mit Swing
• Viele elementare GUI-Bausteine (JLabel, JButton, JSlider, JTable, JColorChooser,...)
• Bausteine werden in GUI-Komponenten zusammengefasst
• Zusammenfassung typisch in JPanel
• Jede Zusammenfassung hat LayoutManager, der Anordnung der einzelnen Komponenten berechnet
• Komponenten können in Komponenten geschachtelt werden; äußere Komponente hat wieder LayoutManager
• konsequentes Schachtel-in-Schachtel-Prinzip
• GUI-Baustein nutzt Model-Delegate-Prinzip; zu jeder Komponente gibt es Model-Objekt, das Eigenschaften speichert
• GUI-Bausteine mit vielen get-/set-Methoden konfigurierbar
Stephan Kleuker 197Software-Qualität
Achtung: Swing und Threads
• Swing-Objekte wie JFrame-Objekte können direkt gestartet werden
• Event-Verwaltung findet in unabhängigen Prozessen statt (genauer: Threads -> Betriebssysteme)
• Problem: Komponenten reden mit noch nicht erzeugten Komponenten ( -> NullPointerException)
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MeineGUI();
}
});
Stephan Kleuker 198Software-Qualität
Minibeispiel: Klickzähler (1/3)
• Zählt Links- und Rechtsklicks des Knopfes
• Für Test notwendig (bzw. sehr sinnvoll), GUI-Elemente haben Namen
JFrameGridLayout(0,2)
JPanelGridLayout(0,2)
JButton„klick“
JLabel„rechts“
JLabel„links“
JFrameJButtonJPanel
JLabelJLabel
GUI immer hierarchisch organisiert
Stephan Kleuker 199Software-Qualität
Minibeispiel: Klickzähler (2/3)public class Klicker extends JFrame implements MouseListener{private JButton klick = new JButton("klick misch");private JLabel links = new JLabel("0");private JLabel rechts = new JLabel("0");
public Klicker(){setLayout(new GridLayout(0,2));setName("klicker");JPanel tmp = new JPanel();tmp.setLayout(new GridLayout(0,2));tmp.add(links);links.setName("links");tmp.add(rechts);rechts.setName("rechts");add(klick);klick.setName("klick");klick.addMouseListener(this);add(tmp);setDefaultCloseOperation(EXIT_ON_CLOSE);pack();setVisible(true);
}
Stephan Kleuker 200Software-Qualität
Minibeispiel: Klickzähler (3/3)
public static void main(String[] args) {javax.swing.SwingUtilities.invokeLater(new Runnable() {public void run() {new Klicker();
}});
}
@Overridepublic void mouseClicked(MouseEvent e) {JLabel ziel=null;if(e.getButton()==1)ziel=links;
else if(e.getButton()==3)ziel=rechts;
if(ziel!=null)ziel.setText(""+(Integer.parseInt(ziel.getText())+1));
}
@Override public void mouseEntered(MouseEvent arg0) {}@Override public void mouseExited(MouseEvent arg0) {}@Override public void mousePressed(MouseEvent arg0) {}@Override public void mouseReleased(MouseEvent arg0) {}
}
Stephan Kleuker 201Software-Qualität
Test des Minibeispiels (1/3)
public class KlickerTest {
private FrameFixture gui;
@BeforeClasspublic static void setUpOnce() { // für Event-VerwaltungFailOnThreadViolationRepaintManager.install();
}
@Beforepublic void setUp() { // für Event-VerwaltungKlicker k = GuiActionRunner.execute(
new GuiQuery<Klicker>() {protected Klicker executeInEDT() {return new Klicker();
}});gui = new FrameFixture(k);// gui.robot.settings().delayBetweenEvents(3000);// gui.show(); // nicht immer erforderlich
}
Stephan Kleuker 202Software-Qualität
Test des Minibeispiels (2/3)
@After public void tearDown() {
gui.cleanUp();}
@Testpublic void testEinLinksklick(){gui.button("klick").click();Assert.assertEquals(gui.label("links").text(), "1");Assert.assertEquals(gui.label("rechts").text(), "0");
}
@Testpublic void testRechtsklick(){gui.button("klick").click(MouseButton.RIGHT_BUTTON);Assert.assertEquals(gui.label("links").text(), "0");Assert.assertEquals(gui.label("rechts").text(), "1");
}
Stephan Kleuker 203Software-Qualität
Test des Minibeispiels (3/3)
@Testpublic void testKeineLinksUndRechtsklicks(){gui.button("klick").click(MouseButton.MIDDLE_BUTTON);gui.button("klick").pressAndReleaseKey(
KeyPressInfo.keyCode(KeyEvent.VK_C));Assert.assertEquals(gui.label("links").text(), "0");Assert.assertEquals(gui.label("rechts").text(), "0");
}
@Testpublic void testVieleLinksUndRechtsklicks(){gui.button("klick").doubleClick(); gui.button("klick").pressAndReleaseKey(
KeyPressInfo.keyCode(KeyEvent.VK_SPACE));for(int i=0;i<10;i++)gui.button("klick").click(MouseButton.RIGHT_BUTTON);
Assert.assertEquals(gui.label("links").text(), "2");Assert.assertEquals(gui.label("rechts").text(), "10");
}}
Stephan Kleuker 204Software-Qualität
Analyse des Minibeispiels
• Einiger konstanter Aufwand um Eventverwaltung zu organisieren
• einfacher Zugriff auf GUI-Elemente wg. setName(.)-Werten
• FrameFixture-Objekt erlaubt Zugriff auf (fast) alle Arten von GUI-Elementen, die über Namen identifiziert werden gui.button("klick")
• Jedes Fixture-Objekt bietet (fast) alle Möglichkeiten zur Bedienung an click(MouseButton.MIDDLE_BUTTON);
• Fixture-Objekte haben Methoden zur Eigenschaftsabfrage gui.label("rechts").text()
Stephan Kleuker 205Software-Qualität
Fest und JUnit 4 - Installation
• benötigte Bibliotheken (bei fest-swing mitgeliefert)
Stephan Kleuker 206Software-Qualität
FEST erkennt Probleme: Schreipfehler
gui.button("klack").click(MouseButton.RIGHT_BUTTON);
Stephan Kleuker 207Software-Qualität
FEST erkennt Probleme: nicht sichtbarer Knopf (1/2)
public class Tapps extends JFrame {
public Tapps(){
JTabbedPane jt = new JTabbedPane();
jt.setName("tabbed");
for(int i=1;i<=3;i++){
JButton jb= new JButton("Tubbi "+i);
jb.setName("tub"+i);
jt.add(jb);
add(jt);
}
pack();
setVisible(true);
}
}
Stephan Kleuker 208Software-Qualität
FEST erkennt Probleme: nicht sichtbarer Knopf (2/2)
@Test public void testTabwechsel(){gui.tabbedPane("tabbed").selectTab("tub2");gui.button("tub2").rightClick();
}
@Testpublic void testTabwechsel2(){ //tub3 nicht sichtbargui.tabbedPane("tabbed").selectTab("tub3");gui.button("tub2").rightClick();
}
Stephan Kleuker 209Software-Qualität
Beispiel systematische FEST-Nutzung (1/3)
• GUI planen; festlegen der GUI-Elemente und ihrer Namen
• Beispiel Tarifzonen-Berechnung
• Entwickler der Tests müssen weitere Implementierungsdetails nicht kennen (black box)
JTextField „zonen“
JComboBox „alter“
JRadioButton „billigUhr“
JRadioButton „teuerUhr“
JCheckBox „zumBetrieb“
JButton „berechnen“
JTextArea „preis“
Stephan Kleuker 210Software-Qualität
Beispiel systematische FEST-Nutzung (2/3)
@Testpublic void testEineZone() {gui.textBox("zonen").enterText("1");gui.button("berechnen").click();Assert.assertEquals(gui.textBox("preis").text()
, "90 Cent");}
@Testpublic void testAlter45() {gui.textBox("zonen").enterText("3");gui.comboBox("alter").selectItem("14-64");gui.button("berechnen").click();Assert.assertEquals(gui.textBox("preis").text()
, "390 Cent");}
Stephan Kleuker 211Software-Qualität
Beispiel systematische FEST-Nutzung (3/3)
@Test
public void testRentnerVormittagsImBetrieb() {
// gui.robot.settings().delayBetweenEvents(3000);
gui.textBox("zonen").enterText("5");
gui.comboBox("alter").selectItem("über 64");
gui.radioButton("billigUhr").click();
gui.checkBox("zumBetrieb").click();
gui.button("berechnen").click();
Assert.assertEquals(gui.textBox("preis").text()
, "305 Cent");
}
Stephan Kleuker 212Software-Qualität
Wann was Testen (1/2)
Generelle Frage nach der Teststrategie
• bottom-up
– zunächst elementare Klassen, dann Klassen die darauf aufbauen
– nur Zusammenspiel der getesteten Objekte testen
– bringt sehr hohe Überdeckung, aber sehr hohen Aufwand
• top-down
– Tests von oben, typisch über Oberfläche ausführen
– benötigt stabiles GUI für Testwiederholung
– vermeintlich weniger Aufwand, weniger Expertise
– häufig sehr geringe Testabdeckung
Stephan Kleuker 213Software-Qualität
Wann was Testen (2/2)
• middle-out
– Tests setzen auf kleinen Gruppen von Klassen auf
– GUI-Tests testen Verbindungen zu Gruppen
– erbt anteilig Vor- und Nachteile der anderen Ansätze
• Variante
– zunächst Tests über GUI durchführen und Überdeckung messen
– Klassen die unter xx% überdeckt werden, dann in Richtung Überdeckungsgrenze genauer analysieren
• generell hängt Test-Strategie von Projektart, vorhandenen Werkzeugen und vorhandenem Know-how ab
Stephan Kleuker 214Software-Qualität
GUI-Elementsuche auch ohne setName
• Generell können „Matcher“ eingesetzt werden, mit denen die nutzende GUI-Komponente bestimmt werden kann@Testpublic void testEineZone2() {gui.textBox(JTextComponentMatcher
.withName("zonen").andText("0")).deleteText();gui.textBox("zonen").enterText("1");GenericTypeMatcher<JButton> textMatcher = new GenericTypeMatcher<JButton>(JButton.class) {@Override protected boolean isMatching(JButton button) {return "Berechnen".equals(button.getText());
}};
gui.button(textMatcher).click(); Assert.assertEquals(gui.textBox("preis").text(), "90 Cent");
}
Stephan Kleuker 215Software-Qualität
Start von Programmen über main (1/2)
• statt Objekt direkt zu nutzen, kann auch Programmstart über main genutzt und Fenster später gesucht werden
public static void main(final String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
Klicker k= new Klicker();
if (args.length>0)
k.setTitle(args[0]);
}
});
}
Stephan Kleuker 216Software-Qualität
Start von Programmen über main (2/2)
@Test
public void testEinLinksklick2(){
ApplicationLauncher.application("klicker.Klicker")
.withArgs("Hai","Wo").start();
FrameFixture frame = WindowFinder.findFrame(
new GenericTypeMatcher<JFrame>(JFrame.class) {
protected boolean isMatching(JFrame frame) {
return "Hai".equals(frame.getTitle())
&& frame.isShowing();
}
}).using(gui.robot);
frame.button("klick").click();
Assert.assertEquals(frame.label("links").text(), "1");
Assert.assertEquals(frame.label("rechts").text(), "0");
}
Stephan Kleuker 217Software-Qualität
Fotos von Fehlersituationen (1/2)
• Weitere Bibliotheken nutzen
@RunWith(GUITestRunner.class)
public class KlickerTest {
// wie sonst auch
@GUITest @Test
public void testEinLinksklickMitMacke(){
gui.button("klick").click();
Assert.assertEquals(gui.label("links").text(), "1");
Assert.assertEquals(gui.label("rechts").text(), "1");
}
Stephan Kleuker 218Software-Qualität
Fotos von Fehlersituationen (2/2)
• Werkzeug macht Screenshot (des gesamten Bildschirms)
Stephan Kleuker 219Software-Qualität
Variante: Bildschirmfotos erstellen lassen
• auch für einzelne Komponenten möglich@Test
public void testMachScreenshot(){
ScreenshotTaker st= new ScreenshotTaker();
gui.button("klick").rightClick();
st.saveDesktopAsPng("rechtsklick.png");
Assert.assertEquals(gui.label("rechts").text(), "1");
}
Stephan Kleuker 220Software-Qualität
Kritische Analyse von FEST
• Vorteile
– relativ einfache Programmierung; Zugang sehr intuitiv
– Tests können ohne weitere Werkzeuge in JUnit umgesetzt werden
– leichte GUI-Änderungen (Layout) benötigen keine Teständerungen
• Nachteile
– Entwickler müssen konsequent setName() nutzen (ok mit Coding-Guidelines)
– Testentwicklung bei Capture & Replay schneller
– Tester müssen Java können
– generelles zentrales Problem: Änderungen des GUIs können zu aufwändigen Teständerungen führen
Stephan Kleuker 221Software-Qualität
Capture & Replay
Variante 2:
• Grundidee: Das Werkzeug zeichnet alle Mausbewegungen und Tastatureingaben auf, die können dann zur Testwiederholung erneut abgespielt werden
• Typisch ist, dass der Nutzer die aufgezeichneten Skripte modifizieren kann (z. B. Test von berechneten Daten)
• Tools können teilweise auch Oberfläche lesen (Frage ob Texte richtig ausgegeben), Snapshots vergleichen
• professionelle Beispiele: Winrunner von HP (früher Mercury), VisualTest von IBM-Rational
Stephan Kleuker 222Software-Qualität
Kurzvorstellung Marathon
• Open Source: http://www.marathontesting.com/Home.html
• Marathon erlaubt die Aufzeichnung und Wiedergabe von Swing-, AWT- und Applet-basierten Oberflächen
• Aufzeichnungen erfolgen in Skriptsprache (Jython, JRuby)
• Zusicherungen und weitere Analysen werden ebenfalls in dieser Skriptsprache ergänzt
• Werkzeug erlaubt Erstellung einfacher Zusicherungen ohne Skriptsprachenkenntnisse
Stephan Kleuker 223Software-Qualität
Marathon-Beispiel (1/6) - Projektkonfiguration
Stephan Kleuker 224Software-Qualität
Marathon-Beispiel (2/6) – Start einer Aufnahme
Stephan Kleuker 225Software-Qualität
Marathon-Beispiel (3/6) - Aufnahme
Aufnahme immer hier beenden
Stephan Kleuker 226Software-Qualität
Marathon-Beispiel (4/6) – resultierendes Skript
• Zusicherung manuell ergänzt
assert_p('<Name des GUI-Elements>', '<Property>', '<erwarteter Wert>')
Property aus
Java-Bean (mit
get und set
bearbeitbar)
Stephan Kleuker 227Software-Qualität
Marathon-Beispiel (5/6) – Ausführen des Skripts
falls zu testendes Programm noch läuft, immer über diesen Knopf beenden
Stephan Kleuker 228Software-Qualität
Marathon-Beispiel (6/6) – Aufnahme mit Zusicherung
während Aufnahme:Strg+Rechtsklick auf zu untersuchendes Element
Stephan Kleuker 229Software-Qualität
Weitere Marathon-Features
• Festlegung von Test-Fixtures, die beschreiben, was vor und nach Tests immer ausführt werden soll
• Module, Programmstücke, die in andere Skripte eingebaut werden können und so eine Strukturierung der Testfälle ermöglichen
• Nutzung eines Debuggers, um ab einem Breakpoint die Ausführung Schritt für Schritt erfolgen zu lassen und ggfls. Objektwerte zu verändern
Stephan Kleuker 230Software-Qualität
Testen von Web-Applikationen - Selenium
• Web-Browser nutzen schwerpunktmäßig HTML zur Darstellung
• Capture & Replay-Werkzeuge, die hardgecoded Pixel und Klicks verarbeiten, eignen sich meist auch für diese Programme
• Einfaches Werkzeug für Web-Applikationen und Firefox ist Selenium (http://seleniumhq.org/)
– erlaubt Capture & Replay von Nutzereingaben
– ermöglicht Tests von Elementen
– erlaubt den Export der aufgezeichneten Tests u. a. in JUnit
– basiert auf JavaScript and Iframes
Stephan Kleuker 231Software-Qualität
Selenium - Aufzeichnen
Stephan Kleuker 232Software-Qualität
Selenium - Zusicherungen
Stephan Kleuker 233Software-Qualität
Selenium - Einbindung in JUnit
• benötigt: http://release.seleniumhq.org/selenium-remote-control/1.0.1/selenium-remote-control-1.0.1-dist.zip
Stephan Kleuker 234Software-Qualität
Grenzen typischer Capture & Replay - Werkzeuge
• Aufzeichnungsprobleme möglich, da nicht alle Events sauber erkannt werden (zu viele, Pixelangabe ungenau)
• Gerade bei unterschiedlichen Web-Browsern kann es durch wenige Pixel zu Bedienproblemen kommen
• Reaktionszeiten von GUIs müssen beachtet werden
• Die Prüfung, ob Texte vollständig angezeigt werden, oder sich GUI-Elemente teilweise überlappen, ist oft nicht möglich
• Innovative, jetzt noch unbekannte Bedienelemente (z. B. Multi-Touch) werden meist noch nicht unterstützt
• Generell: Automatische Prüfung der Software-Ergonomie nicht möglich (-> Usability-Tests)
Stephan Kleuker 235Software-Qualität
Fazit
• GUI-Tests sind generell möglich
• GUI-Tests erst planen, wenn GUI relativ festgelegt ist
• Werkzeuge kritisch evaluieren, ob sie für Projekt bzw. typische Unternehmensaufgaben geeignet sind
• freie GUI-Werkzeuge können auf jeden Fall Einstieg in GUI-Testansätze sein
• Evaluation von kommerziellen Werkzeugen in diesem Bereich oft sinnvoll
• generell wird Teststrategie benötigt, was wann getestet wird (botton up, top down, middle out)