Variante: Automatisierte GUI

Werbung
Variante: Automatisierte GUI-Steuerung
• bisherige Ansätze auf Java eingeschränkt
• generell: Capture & Replay gibt es, oft
programmiersprachenabhängig, in vielen Varianten
• generell: Kosten und Nutzen bzgl. erwarteter Änderungen
überlegen
• Variante: Software steuert die Bedienung anhand von Bildern
• Idee: Bildausschnitte werden zuerst fotografiert, dann bei
Ausführung gesucht; Bildausschnitte können dann geklickt
und über Tastatur gefüllt werden
• Testen: Prüfung, ob erwartete Bildausschnitte gefunden
werden
• hier: Sikuli-API (https://code.google.com/p/sikuli-api/)
Software-Qualität
Stephan Kleuker
274
Testrechner
• Nutzung hängt stark vom Testrechner ab (Geschwindigkeit,
Auflösung, gewähltes GUI-Design, Anzahl angeschlossener
Monitore)
• Ansatz: es gibt feste Testrechner, die zum Testen genutzt
werden, mit klarer Konfiguration
• sinnvolle Variante: Nutzung virtueller Rechner
• keine durchsichtigen Fenster oder Ränder
• Prozessautomatisierung zur Testausführung (aufspielen,
ausführen, Ergebnis einsammeln)
Software-Qualität
Stephan Kleuker
275
Hilfsmittel
• Screencapturer, z. B. FastStone Capture
• Bildanalyseprogramm mit Pixelangaben, z. B. Photofiltre
• ausgehend von links-oben muss relativ (67,11) geklickt werden
http://www.portablefreeware.com/?id=775
http://portableapps.com/apps/graphics_pictures/photofiltre_portable
Software-Qualität
Stephan Kleuker
276
Genereller Ansatz
ScreenRegion s = new DesktopScreenRegion();
DesktopScreenRegion();
File file = new File("
File("bilder
bilder/zonen.png");
bilder/zonen.png");
Target target = new ImageTarget(
ImageTarget(file);
file);
Bildschirm
aufnehmen
zu suchendes Bild
wird zum Ziel
Mindestgenauigkeit mit der
Bild gefunden werden muss
target.setMinScore(0.9
target.setMinScore(0.9);
(0.9);
ScreenRegion r = s.find(
s.find(target);
target);
Mouse mouse = new DesktopMouse();
DesktopMouse();
mouse.click(
mouse.click(r.getCenter());
r.getCenter());
finde passenden Bildausschnitt
mit höchster Genauigkeit; null
wenn nicht gefunden
erzeuge Maus zur Steuerung,
mache einfachen Links-klick
Keyboard keyboard = new DesktopKeyboard
DesktopKeyboard();
();
keyboard.type("33");
keyboard.type("33");
erzeuge Tastatur und gebe an
aktueller Position den Text ein
Software-Qualität
Stephan Kleuker
277
wichtige Varianten
List<ScreenRegion
List<ScreenRegion>
ScreenRegion> sr = s.findall(
s.findall(target);
target);
findet alle Regionen, in denen eine Mindestübereinstimmung
mit dem target gefunden wird, absteigend geordnet
ScreenRegion r = s.find(
s.find(target);
target);
rec = r.getBounds();
r.getBounds();
rec.x = 75;
rec.y = 13;
rec.height = 0;
rec.width = 0;
r.setBounds(
r.setBounds(rec);
rec);
mouse.click(
mouse.click(r.getCenter());
r.getCenter());
ermöglicht die genaue Festlegung des Punktes, auf den geklickt
werden soll
Software-Qualität
Stephan Kleuker
278
Ausschnitt: wichtige Klassen (1/2)
Software-Qualität
Stephan Kleuker
279
Ausschnitt: wichtige Klassen (2/2)
Software-Qualität
Stephan Kleuker
280
Hilfsmittel zur Visualisierung
ScreenRegion s = new DesktopScreenRegion();
DesktopScreenRegion();
Target target = new ImageTarget(
ImageTarget(new File("bilder
File("bilder/Uhrzeit.png
bilder/Uhrzeit.png"));
/Uhrzeit.png"));
ScreenRegion r = s.find(
s.find(target);
target);
Canvas canvas = new DesktopCanvas();
DesktopCanvas(); // Zeichenflaeche
canvas.addBox(r
canvas.addBox(r);
(r);
canvas.addLabel(r
canvas.addLabel(r,
(r, r.getBounds().
r.getBounds().toString
().toString());
toString());
canvas.display(3);
canvas.display(3); // Sekunden
Software-Qualität
Stephan Kleuker
281
Hilfsprogramm zur Analyse (1/3)
import
import
import
import
import
java.io.File;
java.io.File;
java.nio.file.DirectoryStream;
java.nio.file.DirectoryStream;
java.nio.file.Files;
java.nio.file.Files;
java.nio.file.Path;
java.nio.file.Path;
java.nio.file.Paths;
java.nio.file.Paths;
import
import
import
import
import
import
org.sikuli.api.DesktopScreenRegion;
org.sikuli.api.DesktopScreenRegion;
org.sikuli.api.ImageTarget;
org.sikuli.api.ImageTarget;
org.sikuli.api.ScreenRegion;
org.sikuli.api.ScreenRegion;
org.sikuli.api.Target;
org.sikuli.api.Target;
org.sikuli.api.visual.Canvas;
org.sikuli.api.visual.Canvas;
org.sikuli.api.visual.DesktopCanvas;
org.sikuli.api.visual.DesktopCanvas;
public class Analyse {
private final String ORDNER ="bilder/";
Software-Qualität
Stephan Kleuker
282
Hilfsprogramm zur Analyse (2/3)
public void zeigeDetails(String
zeigeDetails(String dateiname){
dateiname){
System.out.println("suche:
System.out.println("suche: " + dateiname);
dateiname);
ScreenRegion s = new DesktopScreenRegion();
DesktopScreenRegion();
//s
//s = new DesktopScreenRegion(
DesktopScreenRegion(-2560,0,700,1000);
Target target = new ImageTarget(
ImageTarget(new File(dateiname
File(dateiname));
dateiname));
target.setMinScore(0.7);
target.setMinScore(0.7);
Canvas canvas = new DesktopCanvas();
DesktopCanvas();
int anzahl = 0;
for (ScreenRegion sc : s.findAll(
s.findAll(target))
target)) {
canvas.addBox(
canvas.addBox(sc);
sc);
canvas.addLabel(
canvas.addLabel(sc,
sc, dateiname+":
dateiname+": "+sc.getScore
"+sc.getScore());
sc.getScore());
anzahl++;
anzahl++;
}
if(
if(anzahl > 0){
canvas.display(8);
canvas.display(8);
} else {
System.out.println("Bild
System.out.println("Bild nicht gefunden");
}
Software-Qualität
Stephan Kleuker
}
283
Hilfsprogramm zur Analyse (3/3)
public void alle(){
Path dir = Paths.get(ORDNER);
Paths.get(ORDNER);
try (DirectoryStream
(DirectoryStream<Path>
DirectoryStream<Path> stream = Files
.newDirectoryStream(
newDirectoryStream(dir,
dir, "*.png
"*.png"))
png")) {
for (Path entry : stream)
stream) {
zeigeDetails(
zeigeDetails(entry.toString());
entry.toString());
}
} catch (Exception
(Exception e) {
System.out.println(e);
System.out.println(e);
}
}
public void eines(String name){
name){
zeigeDetails(ORDNER
zeigeDetails(ORDNER + name +".png
+".png");
png");
}
public static void main(String[] args)
args) {
new Analyse().alle();
}
}
Software-Qualität
Stephan Kleuker
284
Schwellwertanalyse (1/3)
auch genau passende
Ausschnitte liefern
nicht unbedingt 1.0
Software-Qualität
Stephan Kleuker
285
Schwellwertanalyse (2/3)
auch unpassende
Ausschnitte liefern
evtl. hohe Werte
Software-Qualität
Stephan Kleuker
286
Schwellwertanalyse (3/3)
generell möglichst
große, sich
verändernde Flächen
(hier recht klein)
Software-Qualität
Stephan Kleuker
287
Sikuli-Nutzung (1/10): Testarchitektur
Software-Qualität
Stephan Kleuker
288
Sikuli-Nutzung (2/10): benutzte Bilder
• Steuerung
• Ergebnisüberprüfung
Software-Qualität
Stephan Kleuker
289
Sikuli-Nutzung (3/10): zentrale Variablen
public class GUIBedienung {
private
private
private
private
private
String verzeichnis = "bilder
"bilder/";
bilder/";
int offset = 0; // wenn mehrere Monitore angeschlossen
ScreenRegion region;
region;
Mouse mouse;
mouse;
Keyboard keyboard;
keyboard;
Anmerkung: Sikuli-IDE hat ein Positionierungsproblem, wenn
mehr als ein Monitor angeschlossen ist und der erste Monitor
nicht der Hauptbildschirm ist
Software-Qualität
Stephan Kleuker
290
Sikuli-Nutzung (4/10): mehrere Monitore
public GUIBedienung()
GUIBedienung() { // nur bei mehreren Monitoren
GraphicsEnvironment ge = GraphicsEnvironment
.getLocalGraphicsEnvironment();
getLocalGraphicsEnvironment();
GraphicsDevice[]
GraphicsDevice[] gs = ge.getScreenDevices();
ge.getScreenDevices();
GraphicsConfiguration[]
GraphicsConfiguration[] gc = gs[0].
gs[0].getConfigurations
[0].getConfigurations();
getConfigurations();
System.out.println(
System.out.println(gc[0].
gc[0].getBounds
[0].getBounds().
getBounds().getX
().getX());
getX());
if (gc[0].
gc[0].getBounds
[0].getBounds().
getBounds().getX
().getX()
getX() > 0) {
this.offset = (int
(int)
int) gc[0].
gc[0].getBounds
[0].getBounds().
getBounds().getX
().getX();
getX();
}
if (!System.getProperty
(!System.getProperty("os.name").
System.getProperty("os.name").startsWith
("os.name").startsWith("Windows"))
startsWith("Windows")) {
System.err.println("Sorry,
System.err.println("Sorry, nur fuer Windows umgesetzt");
}
this.mouse = new DesktopMouse();
DesktopMouse();
this.keyboard = new DesktopKeyboard();
DesktopKeyboard();
}
Software-Qualität
Stephan Kleuker
291
Sikuli-Nutzung (5/10): Starten (individuell)
// hier liegt zu testendes Programm als JarJar-Datei vor, die
// aufgerufen wird (bestriebssystemabhängig
(bestriebssystemabhängig ).
// auch direkter Aufruf des Konstruktors ggfls. möglich
public void initialisieren() {
String[] cmd = new String[3];
cmd[0]
cmd[0] = "cmd.exe";
cmd[1]
cmd[1] = "/C";
cmd[2]
cmd[2] = "java
"java -jar tarifrechner.jar";
try {
Runtime.getRuntime().
Runtime.getRuntime().exec
().exec(
exec(cmd);
cmd);
Thread.sleep(1000
Thread.sleep(1000);
(1000); // evtl. nicht notwendig
} catch (Exception
(Exception e) {
e.printStackTrace();
e.printStackTrace();
throw new IllegalArgumentException("
IllegalArgumentException("Start
("Start gescheitert");
gescheitert");
}
region = new DesktopScreenRegion(
DesktopScreenRegion(-offset, 0, 300, 275);
}
Software-Qualität
Stephan Kleuker
292
Sikuli-Nutzung (6/10): Hilfsmethoden
private ScreenRegion klickpunkt(ScreenRegion
klickpunkt(ScreenRegion r, int x, int y){
Rectangle rec = r.getBounds();
r.getBounds();
rec.x += offset + x;
rec.y += y;
rec.height = 0;
rec.width = 0;
r.setBounds(
r.setBounds(rec);
rec);
return r;
}
private ScreenRegion findeBild(String
findeBild(String name)
name) {
return findeBild(
findeBild(name,
name, 0.9);
}
private ScreenRegion findeBild(String
findeBild(String name, double score) {
File file = new File(this.verzeichnis
File(this.verzeichnis + name);
name);
Target target = new ImageTarget(
ImageTarget(file);
file);
target.setMinScore(score);
target.setMinScore(score);
return region.find(
region.find(target);
target);
Stephan Kleuker
293
} Software-Qualität
Sikuli-Nutzung (7/10): GUI-Elemente ansteuern
public void beenden() {
ScreenRegion r = findeBild("Beenden.png");
findeBild("Beenden.png");
r = klickpunkt(r, 241, 8);
this.mouse.click(
this.mouse.click(r.getCenter());
r.getCenter());
}
public void zonenEingeben(String
zonenEingeben(String eingabe)
eingabe) {
ScreenRegion r = findeBild("Zonen.png");
findeBild("Zonen.png");
r = klickpunkt(r, 82, 12);
this.mouse.click(
this.mouse.click(r.getCenter());
r.getCenter());
this.keyboard.type(
this.keyboard.type(eingabe);
eingabe);
}
public void alterUeber64Waehlen() {
ScreenRegion r = findeBild("AltersBox.png");
findeBild("AltersBox.png");
r = klickpunkt(r, 157, 12);
this.mouse.click(
this.mouse.click(r.getCenter());
r.getCenter());
r = findeBild("AlterAendern.png");
findeBild("AlterAendern.png");
r = klickpunkt(r, 117, 53);
this.mouse.click(
this.mouse.click(r.getCenter());
r.getCenter());
}
Software-Qualität
Stephan Kleuker
294
Sikuli-Nutzung (8/10): Ergebnis finden
public String berechnenAusfuehren()
berechnenAusfuehren() {
ScreenRegion r = findeBild("Berechnen.png");
findeBild("Berechnen.png");
r = klickpunkt(r, 63, 16);
mouse.click(
mouse.click(r.getCenter());
r.getCenter());
r = findeBild("30Cent.png",
findeBild("30Cent.png", 0.887);
if (r != null)
return "30 Cent";
Cent";
r = findeBild("130Cent.png",
findeBild("130Cent.png", 0.82);
if (r != null)
return "130 Cent";
Cent";
r = findeBild("220Cent.png",
findeBild("220Cent.png", 0.86);
if (r != null)
return "220 Cent";
Cent";
r = findeBild("175Cent.png",
findeBild("175Cent.png", 0.81);
if (r != null)
return "175 Cent";
Cent";
throw new IllegalArgumentException(
IllegalArgumentException(
"Unerwartetes Ergebnis");
}
Software-Qualität
Stephan Kleuker
295
Sikuli-Nutzung (9/10): Tests (1/2) wiederverwandt
// in GuiTarifrechnerTest
private GUIBedienung gui;
gui; // in setUp()
setUp() intialisieren
@Test
public void test1() {
gui.zonenEingeben("1");
gui.zonenEingeben("1");
gui.alterUnter14Waehlen();
gui.teuerUhrWaehlen();
gui.teuerUhrWaehlen();
gui.zumBetriebKlicken();
gui.zumBetriebKlicken();
Assert.assertTrue(
Assert.assertTrue(gui.berechnenAusfuehren()
gui.berechnenAusfuehren()
.equals("30
equals("30 Cent"));
}
@Test
public void test2() {
gui.zonenEingeben("2");
gui.zonenEingeben("2");
gui.alter1464Waehlen();
gui.billigUhrWaehlen();
gui.billigUhrWaehlen();
Assert.assertTrue(
Assert.assertTrue(gui.berechnenAusfuehren()
gui.berechnenAusfuehren()
.equals("130
equals("130 Cent"));
}
Software-Qualität
Stephan Kleuker
296
Sikuli-Nutzung (10/10): Tests (2/2) wiederverwandt
@Test
public void test3() {
gui.zonenEingeben("1");
gui.zonenEingeben("1");
gui.alter1464Waehlen();
gui.billigUhrWaehlen();
gui.billigUhrWaehlen();
gui.zumBetriebKlicken();
gui.zumBetriebKlicken();
Assert.assertTrue(
Assert.assertTrue(gui.berechnenAusfuehren()
gui.berechnenAusfuehren()
.equals("30
equals("30 Cent"));
}
@Test
public void test4() {
gui.zonenEingeben("2");
gui.zonenEingeben("2");
gui.alterUeber64Waehlen();
gui.teuerUhrWaehlen();
gui.teuerUhrWaehlen();
Assert.assertTrue(
Assert.assertTrue(gui.berechnenAusfuehren()
gui.berechnenAusfuehren()
.equals("220
equals("220 Cent"));
}
Software-Qualität
Stephan Kleuker
297
Sikuli-Ausblick
• Sikuli-API mit Potenzial, aber wenig Entwickler
• Variante Sikuli-Script, Sikuli X (http://www.sikuli.org/)
– ähnliches Konzept, aber mit Oberfläche und
Skriptsprache, die das Suchen, Analysieren und Nutzen
von Bildern vereinfacht
– benötigt (e?) Java 6
– Nutzung zusammen mit Eclipse recht aufwändig [Kle]
– aktuell (2014) vielversprechende Aktualisierung
• typisches Weiterentwicklung: Texterkennung
Software-Qualität
Stephan Kleuker
298
Sikuli-Skript
Software-Qualität
Stephan Kleuker
299
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 GUITestansätze sein
• Evaluation von kommerziellen Werkzeugen in diesem
Bereich oft sinnvoll
• (wieder) können Kombinationen von Werkzeugen sinnvoll
sein
• generell wird Teststrategie benötigt, was wann getestet
wird (botton up, top down, middle out)
Software-Qualität
Stephan Kleuker
300
Herunterladen