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