12. Ausblick: Test von Web

Werbung
12. Ausblick: Test von Web-Applikationen
• eigentlich nichts Besonderes
• Selenium DIE
• Selenium WebDriver
– Möglichkeiten
– systematische Tests
Software-Qualität
Stephan Kleuker
405
Test von webbasierten Systemen
• grundsätzlich: viel aufteilbar und getrennt testbar (Beans,
Persistenzanteil), klassisch mit JUnit und Mocks testbar
• verschiedene Varianten mit Test-Servern (oder speziellen
Containern), die in Unit-Frameworks eingebunden werden
• Beispiele: Arquillian als Embedded Container (JBoss)
EJB 3.1: EJBContainer.createEJBContainer();
EJBContainer.createEJBContainer();
• Tests steuerbar über Selenium
• trotzdem gerade im reinen Enterprise-Bereich (ohne GUIs)
bleibt gewisse Unsicherheit
• aktuelle Forschung: Tests verteilt auf mehreren Rechnern
laufen lassen, Ergebnisse einsammeln und komponieren
Software-Qualität
Stephan Kleuker
406
Testen von Web-Applikationen - Selenium
• Web-Browser nutzen schwerpunktsmäß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 IDE (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
• Programmierte Tests mit Selenium WebDriver (zu bevorzugen)
Software-Qualität
Stephan Kleuker
407
Selenium IDE - Aufzeichnen
Software-Qualität
Stephan Kleuker
408
Selenium IDE - Zusicherungen
Software-Qualität
Stephan Kleuker
409
Selenium IDE - Einbindung in JUnit
Software-Qualität
Stephan Kleuker
410
Selenium WebDriver
•
•
•
•
Selenium steuert Browser von Java (C#, Python, Ruby) aus
Installation als jar-Dateien
flexible Möglichkeiten zum Finden von GUI-Komponenten
ideal für Regressionstests, bei wenig sich ändernden GUIs
• in fast allen Unternehmen genutzt, die Web-Applikationen
herstellen
• kontinuierliche Weiterentwicklung (nicht immer alles
übertragbar, Selenium -> Selenium 2)
• Grundregel: nur automatisieren, was sinnvoll und machbar
ist, Rest manuell
• http://docs.seleniumhq.org/docs/
Software-Qualität
Stephan Kleuker
411
Beispielprogramm (1/3)
• Spezifikation: Zu entwickeln ist eine Applikation mit der
geheime Nachrichten an einen Server übergeben und dort
wieder abgefragt werden können. Genauer gibt der Nutzer
eine Nachricht zusammen mit zwei Codewörtern und der
Anzahl, wie oft die Nachricht abgerufen werden kann, ein.
Weiterhin kann ein Nutzer über die Eingabe zweier
Codewörter an gespeicherte Nachrichten kommen. Ist die
Codewortkombination nicht vorhanden, wird ein
Standardtext ausgegeben.
• Realisierung: Glasfish, JSF (Nutzung des Application Scope)
Software-Qualität
Stephan Kleuker
412
Beispielprogramm (2/3)
Server starten
Applikation starten
http://localhost:8080/SecretSafe/
vergebene Ids:
main:verfassen
main:lesen
Software-Qualität
Stephan Kleuker
413
Beispielprogramm (3/3)
Nachricht verfassen
Software-Qualität
Nachricht lesen
Stephan Kleuker
414
Einblick in Nutzungsmöglichkeiten (1/14)
public class SafeMoeglichkeitenTest {
private WebDriver driver;
driver;
private int linie = 0;
@Before
public void setUp()
setUp() {
// Erzeugt neue Instanz des Browser Treibers
// oftmals noch Konfigurationsaufwand
driver = new FirefoxDriver();
FirefoxDriver();
// driver = new HtmlUnitDriver();
HtmlUnitDriver();
// driver = new ChromeDriver();
ChromeDriver();
//driver
//driver = new InternetExplorerDriver();
InternetExplorerDriver();
}
Software-Qualität
Stephan Kleuker
415
Einblick in Nutzungsmöglichkeiten (2/14)
• Klasse WebDriver als zentrale Steuerungsmöglichkeit
• Erzeugt neue Browser-Instanz
• Browser muss auf dem System installiert sein, nutzt keine
weiteren Einstellungen des aktuellen Nutzers (leeres Profil)
• werden kontinuierlich weiterentwickelt
• (früher reichte driver = new InternetExplorerDriver();
InternetExplorerDriver(); )
• bisheriges Angebot (unterschiedliche Qualität):
HtmlUnitDriver(), FirefoxDriver(), InternetExplorerDriver(),
ChromeDriver(),
• OperaDriver durch andere Entwickler, IPhoneDriver nur
zusammen mit Apple-XCode-Umgebung, AndroidDriver mit
Android-Entwicklungsunterstützung
• Hintergrund: Selenium lange Zeit nur mit Firefox nutzbar
Software-Qualität
Stephan Kleuker
416
Einblick in Nutzungsmöglichkeiten (3/14)
• Zentrale Hilfsklasse für GUI-Komponenten: WebElement
• nur zur Veranschaulichung: Ausgabemöglichkeit
private void zeigeElemente(List<
zeigeElemente(List<WebElement
(List<WebElement>
WebElement> liste){
System.out.println("
System.out.println("---("----"+(++
----"+(++linie
"+(++linie));
linie));
if (liste != null) {
for (WebElement w : liste) {
System.out.println("
System.out.println(" " + w.getTagName()
w.getTagName()
+ "::" + w.getAttribute("type")
w.getAttribute("type")
+ "::" + w.getAttribute("
w.getAttribute("name
("name")
name")
+ "::" + w.getAttribute("
w.getAttribute("value
("value")
value")
+ "::" + w.getText()
w.getText()
+ "::" + w.getLocation()
w.getLocation() + "::" +w.isEnabled
+w.isEnabled());
w.isEnabled());
}
}
}
Software-Qualität
Stephan Kleuker
417
Einblick in Nutzungsmöglichkeiten (4/14)
• Überblick über generierte HTML-Seite
• In Entwicklung sinnvolle Ids/Namen vergeben
• JSF: Ids eindeutig
• Zugriff auch ohne Ids machbar („drittes Imput-Element“)
Software-Qualität
Stephan Kleuker
418
Einblick in Nutzungsmöglichkeiten (5/14)
@Test
public void testBeispielvarianten
testBeispielvarianten()
()
throws InterruptedException,
InterruptedException, IOException {
// Seite aufrufen
driver.get("http://localhost:8080/
driver.get("http://localhost:8080/SecretSafe
("http://localhost:8080/SecretSafe/");
SecretSafe/");
List<WebElement
List<WebElement>
WebElement> liste =
driver.findElements(
driver.findElements(By.tagName("
By.tagName("input
("input"));
input"));
zeigeElemente(liste
zeigeElemente(liste);
(liste);
----1
----1
input::hidden::main::main::::(0,
input::hidden::main::main::::(0, 0)::true
input::submit::main:verfassen::Nachricht verfassen::::(8, 129)::true
input::submit::main:lesen::Nachricht lesen::::(8, 153)::true
input::hidden::javax.faces.ViewState::2158484851038199978:input::hidden::javax.faces.ViewState::2158484851038199978:1608245938470041174::::(0, 0)::true
Software-Qualität
Stephan Kleuker
419
Einblick in Nutzungsmöglichkeiten (6/14)
List<WebElement
driver.findElements(
(
List<WebElement>
WebElement> inp = driver.findElements
By.xpath("//
By.xpath("//input
("//input"));
input"));
zeigeElemente(
zeigeElemente(inp);
inp);
----2
----2
input::hidden::main::main::::(0,
input::hidden::main::main::::(0, 0)::true
input::submit::main:verfassen::Nachricht verfassen::::(8, 129)::true
input::submit::main:lesen::Nachricht lesen::::(8, 153)::true
input::hidden::javax.faces.ViewState::2158484851038199978:input::hidden::javax.faces.ViewState::2158484851038199978:1608245938470041174::::(0, 0)::true
Software-Qualität
Stephan Kleuker
420
Einblick in Nutzungsmöglichkeiten (7/14)
• Viele weitere Lokalisierungsmöglichkeiten
Method Summary
static By className(java.lang.String className)
static By cssSelector(java.lang.String selector)
WebElement findElement(SearchContext context)
List<WebElement> findElements(SearchContext context)
static By id(java.lang.String id)
static By linkText(java.lang.String linkText)
static By name(java.lang.String name)
static By partialLinkText(java.lang.String linkText)
static By tagName(java.lang.String name)
static By xpath(java.lang.String xpathExpression)
http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/B
y.html
Software-Qualität
Stephan Kleuker
421
Einblick in Nutzungsmöglichkeiten (8/14)
• Steuerungsmöglichkeiten mit submit(), click(), weiteren
Eingabemöglichkeiten
WebElement element =
driver.findElement(By.name
driver.findElement(By.name("
(By.name("main:verfassen
("main:verfassen"));
main:verfassen"));
System.out.println(
System.out.println(element.getTagName()
element.getTagName()
+ "::" + element.getAttribute("type")
element.getAttribute("type")
+ "::" + element.getAttribute("
element.getAttribute("name
("name")
name")
+ "::" + element.getAttribute("
element.getAttribute("value
("value"));
value"));
element.click();
element.click();
input::
input::submit
::submit::
submit::main:verfassen
::main:verfassen::Nachricht
main:verfassen::Nachricht verfassen
Software-Qualität
Stephan Kleuker
422
Einblick in Nutzungsmöglichkeiten (9/14)
System.out.println(
System.out.println(driver.findElement(
driver.findElement(By.tagName("
By.tagName("body
("body"))
body"))
.getText());
getText());
Codewort 1:
Codewort 2:
geheime Nachricht:
wie oft abrufbar:
Zur Startseite
// Hilfsvariable für folgende Berechnung
List<WebElement
List<WebElement>
WebElement> labels =
driver.findElements(
driver.findElements(By.tagName("
By.tagName("input
("input"));
input"));
Software-Qualität
Stephan Kleuker
423
Einblick in Nutzungsmöglichkeiten (10/14)
• es besteht die Möglichkeit JavaScript direkt auszuführen
• Mächtige Möglichkeiten, z. B. um Skripte zu starten oder
Seite zu analysieren
• hier mit Übergabe und Rückgabe
@SuppressWarnings("
SuppressWarnings("unchecked
("unchecked")
unchecked")
List<WebElement
List<WebElement>
WebElement> inputs2 = (List<WebElement
(List<WebElement>)
WebElement>)
((JavascriptExecutor
((JavascriptExecutor)
JavascriptExecutor)driver).
driver).executeScript
).executeScript(
executeScript(
"var labels = arguments[0];
arguments[0]; "
+ "var
"var inputs = []; "
+ "for
"for (var i=0; i < labels.length;
labels.length; i++){"
+ " inputs.push(
inputs.push(document.getElementById("
document.getElementById("
+ "
labels[i].
labels[i].getAttribute
[i].getAttribute('
getAttribute('name
('name')));
name'))); "
+ "} "
+ "return
"return inputs;",
inputs;", labels);
labels);
zeigeElemente(inputs2
zeigeElemente(inputs2);
(inputs2);
Software-Qualität
Stephan Kleuker
424
Einblick in Nutzungsmöglichkeiten (11/14)
• Ausgabe zur letzten Folie
form::null::eingabe
form::null::eingabe::null::Codewort
eingabe::null::Codewort 1:
Codewort 2:
geheime Nachricht:
wie oft abrufbar:
Zur Startseite::(8, 109)::true
109)::true
input::
input::text
::text::eingabe:c1::::::(92,
text::eingabe:c1::::::(92, 109)::true
109)::true
input::
input::text
::text::eingabe:c2::::::(92,
text::eingabe:c2::::::(92, 131)::true
131)::true
input::
input::text
::text::
text::eingabe:geheim
::eingabe:geheim::::::(138,
eingabe:geheim::::::(138, 153)::true
153)::true
input::
input::text
::text::
text::eingabe:ab
::eingabe:ab::0::::(120,
eingabe:ab::0::::(120, 175)::true
175)::true
input::
input::submit
::submit::
submit::eingabe:verschicken
::eingabe:verschicken::Verschicken::::(8,
eingabe:verschicken::Verschicken::::(8,
197)::true
197)::true
Software-Qualität
Stephan Kleuker
425
Einblick in Nutzungsmöglichkeiten (12/14)
Object[]
Object[] werte = {"input
{"input",
input", "text
"text"};
text"};
@SuppressWarnings("
SuppressWarnings("unchecked
("unchecked")
unchecked")
List<WebElement
List<WebElement>
(List<WebElement>)
WebElement> inputs3 = (List<WebElement
WebElement>)
((JavascriptExecutor
((JavascriptExecutor)
JavascriptExecutor) driver).
driver).executeScript
).executeScript(
executeScript(
"var tmp = document.getElementsByTagName(
document.getElementsByTagName(arguments[0]);
arguments[0]); "
+ "var
"var erg = []; "
+ "for
"for (var i=0; i<tmp.length
i<tmp.length;
tmp.length; i++){"
+ " if(
if(tmp[i].type==
tmp[i].type==arguments
[i].type==arguments[1]){"
arguments[1]){"
+ "
erg.push(
erg.push(tmp[i])"
tmp[i])"
+ " }"
+ "}; "
+ "return
"return erg;",
erg;", werte);
werte);
input::
input::text
::text::eingabe:c1::::::(92,
text::eingabe:c1::::::(92, 109)::true
109)::true
input::
input::text
131)::true
::text::eingabe:c2::::::(92,
text::eingabe:c2::::::(92, 131)::true
input::
input::text
::text::
text::eingabe:geheim
::eingabe:geheim::::::(138,
eingabe:geheim::::::(138, 153)::true
153)::true
input::
input::text
::text::
text::eingabe:ab
::eingabe:ab::0
eingabe:ab::0::::(
::0::::(120,
::::(120, 175)::true
175)::true
Software-Qualität
Stephan Kleuker
426
Einblick in Nutzungsmöglichkeiten (13/14)
// Gibt Seitentitel auf Konsole aus
System.out.println("Titel
System.out.println("Titel der Seite ist: "
+ driver.getTitle());
driver.getTitle());
// Bildschirmfoto
File screenshot = ((TakesScreenshot
((TakesScreenshot)
TakesScreenshot)driver)
driver)
.getScreenshotAs(
getScreenshotAs(OutputType.FILE);
OutputType.FILE);
FileUtils.copyFile(
FileUtils.copyFile(screenshot,
screenshot,
new File("bild
File("bild"+
bild"+new
"+new Date().getTime
Date().getTime()+".
getTime()+".png
()+".png"));
png"));
Assert.assertTrue(
Assert.assertTrue(driver.getTitle().
driver.getTitle().contains
().contains("
contains("Pssst
("Pssst"));
Pssst"));
}
Software-Qualität
Stephan Kleuker
427
Einblick in Nutzungsmöglichkeiten (14/14)
• nach mehren Testläufen
Software-Qualität
Stephan Kleuker
428
Projektaufbau
• zentral benötigte Bibliotheken im Selenium-Download
(alle) benötigt
benötigt
Software-Qualität
Stephan Kleuker
429
Weitere Funktionalität
•
•
•
•
•
•
•
Wechsel zwischen Fenstern und zwischen Frames
Möglichkeit vorwärts und zurück zu navigieren
Nutzung von Cookies
(Versuch der) Unterstützung von Drag und Drop
Proxy-Nutzung
Einstellung von Wartezeiten
Warten auf das Erscheinen von HTML-Elementen (wichtig in
Richtung AJAX und HTML5)
• Zusammenspiel mit Selenium IDE zur Testaufzeichnung
Software-Qualität
Stephan Kleuker
430
Achtung: Viele Einstiegsfallen
• generell gute Einarbeitungsmöglichkeit durch gute
Dokumentation
• trotzdem viele kleine Fehlerquellen, die
Entwicklungsprozess bremsen können
• Beispiel: Tests ziehen auf anderen Rechner um
• wichtiges Detail aus der Doku "The browser zoom level
must be set to 100% so that the native mouse events can be
set to the correct coordinates." (nicht erster GoogleTreffer)
• teilweise weitere Browser-Einstellungen beachten
• Fazit: Testrechner nie zu anderen Zwecken nutzen,
Konfiguration sichern
Software-Qualität
Stephan Kleuker
431
Weitere Steuerungsvarianten
• Warten, bis Element vorhanden ist
(new WebDriverWait
WebDriverWait(
(driver,
driver, 10)).
10)).until
)).until(
until(
new ExpectedCondition<
ExpectedCondition<WebElement>(){
WebElement>(){
@Override
public WebElement apply(
apply(WebDriver d) {
return d.findElement(
d.findElement( By.name("j_idt8:j_idt10"));
}});
• Steuerungsvariante mit JavaScript
WebElement but =
driver.findElement(By.name
driver.findElement(By.name("j_idt8:j_idt10"));
(By.name("j_idt8:j_idt10"));
((IJavaScriptExecutor
((IJavaScriptExecutor)
IJavaScriptExecutor)driver)
driver)
.executeScript("
xecuteScript("arguments
("arguments[0].
arguments[0].fireEvent
[0].fireEvent('
fireEvent('onclick
('onclick');",
onclick');", but);
Software-Qualität
Stephan Kleuker
432
Test des Beispiels (1/6)
public class SecretSafeTest {
private WebDriver driver;
driver;
private static String text1;
private static String text2;
@BeforeClass
public static void setupClass()
setupClass() {
text1 = "" + Math.random();
Math.random(); // nicht ganz QSQS-sauber
text2 = "" + Math.random();
Math.random();
}
@Before
public void setUp()
setUp() {
driver = new FirefoxDriver();
FirefoxDriver();
}
@After
public void tearDown()
tearDown() {
driver.quit();
driver.quit();
} Software-Qualität
Stephan Kleuker
433
Test des Beispiels (2/6)
private void startSeite(){
startSeite(){
driver.get("http://
driver.get("http://localhost:8080/
("http://localhost:8080/SecretSafe
localhost:8080/SecretSafe");
SecretSafe");
}
private void eingabeSeite(){
eingabeSeite(){
startSeite();
startSeite();
driver.findElement(By.name("
driver.findElement(By.name("main:verfassen
(By.name("main:verfassen")).
main:verfassen")).click
")).click();
click();
}
private void ausgabeSeite(){
ausgabeSeite(){
startSeite();
startSeite();
driver.findElement(By.name
driver.findElement(By.name("
(By.name("main:lesen
("main:lesen")).
main:lesen")).click
")).click();
click();
}
private void feldFuellen(String
feldFuellen(String name,
name, String wert){
driver.findElement(By.name(
driver.findElement(By.name(name
(By.name(name)).
name)).clear
)).clear();
clear();
driver.findElement(By.name(
driver.findElement(By.name(name
(By.name(name)).
name)).sendKeys
)).sendKeys(wert);
sendKeys(wert);
}
Software-Qualität
Stephan Kleuker
434
Test des Beispiels (3/6)
private void textEingeben
textEingeben(String
(String text1, String text2,
String geheim, int versuche){
eingabeSeite();
eingabeSeite();
feldFuellen("eingabe:c1",text1);
feldFuellen("eingabe:c1",text1);
feldFuellen("eingabe:c2",text2);
feldFuellen("eingabe:c2",text2);
feldFuellen("
feldFuellen("eingabe:geheim
("eingabe:geheim",geheim);
eingabe:geheim",geheim);
feldFuellen("
feldFuellen("eingabe:ab
("eingabe:ab",""+versuche);
eingabe:ab",""+versuche);
driver.findElement(By.name
driver.findElement(By.name("
(By.name("eingabe:verschicken
("eingabe:verschicken")).
eingabe:verschicken")).click
")).click();
click();
Assert.assertTrue(
Assert.assertTrue(driver.findElement(
driver.findElement(By.tagName("
By.tagName("body
("body"))
body"))
.getText().
getText().contains
().contains("Eintrag
contains("Eintrag erfolgreich"));
}
private void textEingeben(String
textEingeben(String geheim, int versuche){
textEingeben(text1
textEingeben(text1,
(text1, text2, geheim, versuche);
}
Software-Qualität
Stephan Kleuker
435
Test des Beispiels (4/6)
private void textErfolgreichSuchen
textErfolgreichSuchen(String
(String text1,
String text2, String geheim){
ausgabeSeite();
ausgabeSeite();
feldFuellen("abfrage:c1",text1);
feldFuellen("abfrage:c1",text1);
feldFuellen("abfrage:c2",text2);
feldFuellen("abfrage:c2",text2);
driver.findElement(By.name
driver.findElement(By.name("
(By.name("abfrage:abfragen
("abfrage:abfragen")).
abfrage:abfragen")).click
")).click();
click();
Assert.assertTrue(
Assert.assertTrue(driver.findElement(
driver.findElement(By.tagName("
By.tagName("body
("body"))
body"))
.getText().
getText().contains
().contains(geheim));
contains(geheim));
}
private void textErfolglosSuchen(String
textErfolglosSuchen(String text1, String text2){
ausgabeSeite();
ausgabeSeite();
feldFuellen("abfrage:c1",text1);
feldFuellen("abfrage:c1",text1);
feldFuellen("abfrage:c2",text2);
feldFuellen("abfrage:c2",text2);
driver.findElement(By.name
driver.findElement(By.name("
(By.name("abfrage:abfragen
("abfrage:abfragen")).
abfrage:abfragen")).click
")).click();
click();
Assert.assertTrue(
Assert.assertTrue(driver.findElement(
driver.findElement(By.tagName("
By.tagName("body
("body"))
body"))
.getText().
getText().contains
().contains("Treffen
contains("Treffen um 730 in KN2"));
}
Software-Qualität
Stephan Kleuker
436
Test des Beispiels (5/6)
private void textErfolgreichSuchen(String
textErfolgreichSuchen(String geheim){
textErfolgreichSuchen(text1,
textErfolgreichSuchen(text1, text2, geheim);
}
private void textErfolglosSuchen(){
textErfolglosSuchen(){
textErfolglosSuchen(text1,
textErfolglosSuchen(text1, text2);
}
@Test
public void testErfolglos(){
testErfolglos(){
textErfolglosSuchen();
textErfolglosSuchen();
}
@Test
public void testEintragenUndLesen(){
testEintragenUndLesen(){
textEingeben("
textEingeben("TextText
("TextText",
TextText", 3);
textErfolgreichSuchen("
textErfolgreichSuchen("TextText
("TextText");
TextText");
textErfolgreichSuchen("
textErfolgreichSuchen("TextText
("TextText");
TextText");
textErfolgreichSuchen("
textErfolgreichSuchen("TextText
("TextText");
TextText");
textErfolglosSuchen();
textErfolglosSuchen();
Stephan Kleuker
} Software-Qualität
437
Test des Beispiels (6/6)
@Test
public void testLinkLesen(){
testLinkLesen(){
ausgabeSeite();
ausgabeSeite();
driver.findElement(
driver.findElement(By.linkText("Zur
By.linkText("Zur Startseite")).click
Startseite")).click();
click();
Assert.assertTrue(
Assert.assertTrue(driver.findElement(
driver.findElement(By.tagName("
By.tagName("body
("body"))
body"))
.getText().
getText().contains
().contains("Was
contains("Was wollen Sie machen?"));
}
@Test
public void testLinkSchreiben(){
testLinkSchreiben(){
eingabeSeite();
eingabeSeite();
driver.findElement(
driver.findElement(By.linkText("Zur
By.linkText("Zur Startseite")).click
Startseite")).click();
click();
Assert.assertTrue(
Assert.assertTrue(driver.findElement(
driver.findElement(By.tagName("
By.tagName("body
("body"))
body"))
.getText().
getText().contains
().contains("Was
contains("Was wollen Sie machen?"));
}
// weitere Tests sinnvoll
}
Software-Qualität
Stephan Kleuker
438
Testarchitektur
• Tests müssen für Änderbarkeit konzipiert werden
• häufig: viele Tests für eine konkrete Version geschrieben,
nach leichten Änderungen der zu testenden Software
werden Tests als unwartbar weggelegt
• Problem: ähnlich wie Software-Architektur wird TestArchitektur benötigt
• ein Ansatz: jeweils eigene Steuerungsklasse für eine WebSeite, Tests arbeiten nur auf diesem Abstraktionslevel
• kleine Änderungen führen zu kleinen Änderungen in der
Steuerungsklasse und keinen Änderungen bei deren Nutzern
• durch Abstraktion muss nicht jeder Tester Selenium können
• -> Tests müssen von qualifizierten Software-Entwicklern
geschrieben werden
Software-Qualität
Stephan Kleuker
439
Design-Tests
• Browser stellen identische Inhalte leicht verändert da
• technisch überflüssig, aber wichtig für den Zeitgeist:
modische Design-Anpassungen
• Für IT-Profi: Sisyphos-Arbeit; Test mit unterschiedlichen
Browsern
• Direkte Hilfsmittel:
– Lunascape: ein Browser, bei dem man zwischen drei
Maschinen umschalten kann IE (Trident)+Firefox
(Gecko)+Chrome, Safari (Webkit)
– Windows: USB-portable Browser ohne
Installationsnotwendigkeit (verändern keine
Einstellungen): Firefox, Chrome, Opera, …
• evtl. auch Capture & Replay mit Selenium zum inhaltlichen
Test
Software-Qualität
Stephan Kleuker
440
Variante: Klassen zur Browsersteuerung (1/2)
public class GoogleTest {
private WebDriver driver;
driver; // (auch Selenium)
public GoogleTest()
GoogleTest() {}
@Before
public void setUp()
setUp() {
//Erzeugt
//Erzeugt neue Instanz des Browser Treibers
//driver
//driver = new FirefoxDriver();
FirefoxDriver();
driver = new InternetExplorerDriver();
InternetExplorerDriver();
//driver
//driver = new HtmlUnitDriver();
HtmlUnitDriver();
//driver
//driver = new ChromeDriver();
ChromeDriver();
}
@After
public void tearDown()
tearDown() {
//beendet
//beendet Browser
driver.quit();
driver.quit();
}
Software-Qualität
Stephan Kleuker
441
Variante: Klassen zur Browsersteuerung (2/2)
@Test
public void testGoogle1() {
//google
//google aufrufen.
aufrufen. "http://" nicht vergessen
driver.get("http://www.google.de/");
driver.get("http://www.google.de/");
//findet das Eingabefeld fuer Suchbegriffe
WebElement element = driver.findElement(By.name("q"));
driver.findElement(By.name("q"));
//gibt
//gibt Suchbegriff "Selenium" ein und bestätigt
element.sendKeys("Selenium");
element.sendKeys("Selenium");
element.submit();
element.submit();
// statt "submit"
submit" auf "Google"Google-Suche"
Suche" button zu clicken:
clicken:
// element = driver.findElement(By.name("
driver.findElement(By.name("btnG
(By.name("btnG"));
btnG"));
// element.click();
element.click();
//Gibt
//Gibt Seitentitel auf Konsole aus
//Ueberprueft
//Ueberprueft ob die Seite "Google" im Titel enthaelt
Assert.assertTrue(
Assert.assertTrue(driver.getTitle().
driver.getTitle().contains
().contains("Google"));
contains("Google"));
}
}
Software-Qualität
Stephan Kleuker
442
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)
Software-Qualität
Stephan Kleuker
443
Erinnerung: Entwicklung und Test
• Integrationsnotwendigkeit von Entwicklung und Test
Programmierregeln müssen auf Testbarkeit abgestimmt sein
• Beispiel: öffentliche get- und set-Methoden für alle
Objektvariablen
• Warum: einfache Herstellung präziser Testszenarien
Werkzeugauswahl zur Entwicklung muss mit Werkzeugauswahl
zum Testen abgestimmt sein
• Beispiel: GUI-Entwicklungswerkzeug soll eindeutige
Identifikatoren der GUI-Elemente unterstützen
• Warum: Einfache klare Ansteuerung von GUI-Elementen
über Ids in Tests möglich; GUI-Änderungen führen nur zu
k(l)einen Änderungen in Testfällen
Software-Qualität
Stephan Kleuker
444
Performancemessung von Web-Applikationen
• Apache JMeter
• Möglichkeit einzelne Web-Aufrufe zu spezifizieren
• die Aufrufe können dann zu einem Testplan kombiniert
werden
• man kann dann festlegen, wie oft die Aufrufe zum Server
geschickt werden; die Antwortzeiten werden gemessen
Software-Qualität
Stephan Kleuker
445
Ausblick: Performancemesseung mit Apache JMeter (1/4)
Default-Einstellung des Servers
Software-Qualität
Stephan Kleuker
446
Ausblick: Performancemesseung mit Apache JMeter (2/4)
Aufruf eines Web-Services zum Einfügen eines Mitarbeiters
Software-Qualität
Stephan Kleuker
447
Ausblick: Performancemesseung mit Apache JMeter (3/4)
Löschen eines Mitarbeiters
Testet man keinen WebService mit direktem Zugriff, wird bei Web-Applikationen
der Proxy von JMeter genutzt, den man dann in einem Browser einstellt, dessen
Aktionen dann von JMeter aufgezeichnet und können für Tests genutzt werden
Software-Qualität
Stephan Kleuker
448
Ausblick: Performancemesseung mit Apache JMeter (4/4)
Ergänzung von Ablaufmöglichkeiten (if, while), Zufallszahlen
Software-Qualität
Stephan Kleuker
449
Herunterladen