Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... ·...

51
Geister, Gurken und Halbmetalle Tools für Web-UI-Acceptance-Tests Malte Clasen http://malteclasen.de

Transcript of Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... ·...

Page 1: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Geister, Gurken und Halbmetalle Tools für Web-UI-Acceptance-Tests

Malte Clasen http://malteclasen.de

Page 2: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 2

Folien und Code

http://malteclasen.de/blog

Page 3: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 3

Problem: Spezifikation

• typischer Fall: Mündliche Überlieferung

Page 4: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 4

Beispiel: Rezept-Ansicht

Page 5: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 5

Beispiel: Rezept-Editor

Page 6: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 6

Demo: Rezept-Editor

Page 7: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 7

Problem: Was passiert wenn...

• ...mehr als drei Kategorien hinzugefügt werden

sollen?

(Layout-Begrenzung)

• ...eine Menge ohne Zutat eingegeben wird?

(ungültige Daten)

• ...der Titel einer Komponente geändert wird?

(interaktive Aktualisierung)

• ...

Page 8: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 8

Flow

Foto: Charles J Sharp; licensed under the Creative Commons Attribution 2.5 Generic license; http://commons.wikimedia.org/wiki/File:South-Goodwin.jpg

Page 9: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 9

Flow-Voraussetzungen

deutliche Ziele (unmittelbare

Rückmeldung)

Konzentration weder

Unter- noch Überforderung

Page 10: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 10

Idee: Wenn-Dann-Liste

• „Wenn ich mehr als drei Kategorien eingeben will,

müssen da eben mehr Felder hin.“

• Problem: Woran erkennt man den Willen?

deutliche Ziele (unmittelbare

Rückmeldung)

Page 11: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 11

konkretisierte Wenn-Dann-Liste

• nur tatsächliche Handlungen erfassen

• nur tatsächliche Ergebnisse prüfen

Page 12: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 12

Beispiel: Kategorien

• „Wenn ich alle Kategorie-Felder ausgefüllt habe,

soll ein zusätzliches leeres erscheinen.“

• „Wenn ich ein Feld leere, dann soll es

verschwinden.“

• ...

Page 13: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 13

Problem: Automatisiert Testen

• automatischer Abgleich von natürlichsprachiger

Spezifikation und Produkt nicht trivial

deutliche Ziele (unmittelbare Rückmeldung)

Page 14: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 14

formalisierte Wenn-Dann-Liste: Gherkin

Scenario: Dynamically Add Category Fields

Given I am on the "Streuselkuchen" recipe page

And I am in edit mode

When I click on the empty category field

And I type "Blechkuchen"

Then there should be 4 category fields

And there should be an empty category field

• in vielen Sprachen verfügbar • für alle lesbar, aber praktisch nur für Programmierer schreibbar

Page 15: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 15

Implementierung

[When(@"I type ""(.*)""")] public void WhenIType(string text) { CurrentElement.SendKeys(text); }

Page 16: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 16

Gherkin-Compiler

• Ruby: Cucumber

http://cukes.info/

• Java: Cucumber-JVM

https://github.com/cucumber/cucumber-jvm

• .net: SpecFlow

http://www.specflow.org/specflownew/

Page 17: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 17

Demo: SpecFlow

Page 18: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 18

Problem: Prozess

• Spezifikation sollte vor Implementierung existieren.

• Test könnte prinzipiell vor Implementierung

existieren. Ist das sinnvoll?

Page 19: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 19

Write failing

acceptance test

Ansatz: Behavior-Driven Development

Make unit test

pass

Refactor

Write failing

unit test

Page 20: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 20

Problem: Detailgrad bei Tests

[When(@"I click on the first preparation text")]

public void

WhenIClickOnTheFirstPreparationComponentText()

{

CurrentElement = FindDisplayed(By.XPath(

"//form[@id='RecipeEditor']//

h2[.='Zubereitung']/following-sibling::h3[1]/

following-sibling::div

[@contenteditable='true'][1]"));

CurrentElement.Click();

}

Page 21: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 21

Ansatz: Abstraktion

• mögliche Lösung: Abstraktion über HTML-

Ausgabe

• Problem: Wird nur für Acceptance-Tests benötigt,

dafür oft zu schwergewichtig

Page 22: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 22

Ansatz: Platzhalter-Tests

• Fake-Test-Driven:

[When(@"I click on the first preparation text")]

public void

WhenIClickOnTheFirstPreparationComponentText()

{

Assert.Fail();

}

Page 23: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 23

Motivation

• Entwicklung erfolgt auf Unit-Test-Ebene

• formalisierte Spezifikation als Ziel ausreichend

• HTML-Interface profitiert nur bedingt von TDD,

Ziel sind Designer und Suchmaschinen

Write failing

acceptance test

Make unit test

pass

Refactor

Write failing

unit test

Page 24: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 24

Tools: Test-Framework

• Java • JUnit, http://junit.org/

• TestNG, http://testng.org/doc/

• .net • MSTest

• nUnit, http://www.nunit.org/

• xUnit.net, http://xunit.codeplex.com/

Page 25: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 25

Tools: Kontinuierliche Tests

• Java: Infinitest

http://infinitest.github.io/

• .net: NCrunch

http://www.ncrunch.net/

deutliche Ziele (unmittelbare Rückmeldung)

Page 26: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 26

Demo: NCrunch

Page 27: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 27

Problem: Test-Implementierung

• Wie lässt sich prüfen, ob eine Web-Anwendung der

Spezifikation entspricht?

Page 28: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 28

Ansatz: Browser-Fernsteuerung

• Spezifikation aus Anwender-Sicht geschrieben

• Anwender interagieren mit Browser

• Test sollte Spezifikation mit Browser prüfen

Page 29: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 29

Tools: Browser-Fernsteuerung

• Selenium

http://docs.seleniumhq.org/

• unterstützt gängige Desktop-Browser • Firefox

• Chrome

• Internet Explorer

Page 30: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 30

Navigation

[Given(@"I am on the homepage")]

public void GivenIAmOnTheHomepage()

{

Web.Navigate().GoToUrl(WebUiContext.RootUrl);

WebUiContext.CatchLog();

}

Page 31: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 31

JavaScript injizieren

public void CatchLog()

{

_driver.ExecuteScript(@"console.defaultLog=console.log;

console.log=function(msg){console.defaultLog(msg);

console.logFile+=msg+'\n';};");

}

public string Log

{

get { return (string)_driver.ExecuteScript(

@"return console.logFile;"); }

}

Page 32: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 32

Mausklicks

[When(@"I follow the ""(.*)"" link")]

public void WhenIFollowTheLink(string linkText)

{

Web.FindElementByLinkText(linkText).Click();

}

Page 33: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 33

Tastatureingaben

[When(@"I type ""(.*)""")]

public void WhenIType(string text)

{

CurrentElement.SendKeys(text);

}

Page 34: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 34

Abfragen des DOM über XPath

[Then(@"there should be the component ""(.*)""")]

public void ThenThereShouldBeTheComponent(string

title)

{

EnsureIsInViewOrPreview();

Web.FindElements(By.XPath(ViewXPathPrefix +

"//h3"))

.Should().Contain(e => e.Text == title);

}

Page 35: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 35

Code-Sharing für View und Editor

private string ViewXPathPrefix

{

get

{

EnsureIsInViewOrPreview();

return IsInView

? "//div[@id='Recipe']"

: "//form[@id='RecipeEditor']";

}

}

Page 36: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 36

Beispiel: Rezept-Abfrage

And the component "Teig" should contain 400 g Mehl

<h2>Zutaten</h2> <h3>Teig</h3> <table class="table table-condensed"> <tbody> <tr class="ingredient"> <td class="amount">400</td> <td class="unit">g</td> <td class="name"> <a href="/Mehl">Mehl</a></td> </tr> </tbody> </table>

Page 37: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 37

Beispiel: Rezept-Abfrage, Code

[Then(@"the component ""(.*)"" should contain (.*) (.*) (.*)")]

public void ThenTheComponentShouldContain(string component,

float amount, string unit, string ingredientName)

{

var classFilter = IsInView ? ""

: string.Format("[contains(@class, '{0}')]", EditClass);

Web.FindElements(By.XPath( string.Format(ViewXPathPrefix +

"//h2[.='Zutaten']/following-sibling::h3[.='{0}']/" +

"following-sibling::table{4}//tr[td[1][.='{1}']" +

" and td[2][.='{2}'] and td[3][.='{3}']]",

component, amount, unit, ingredientName, classFilter)))

.Should().NotBeEmpty();

}

Page 38: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 38

XPath-Ausdrücke

• konkrete Elemente:

FireBug: Inspect Element, Copy XPath

• parametrisierte Ausdrücke: Debugger

Page 39: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 39

Praxis: Chrome-Ausführung

Page 40: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 40

Demo: Chrome via Selenium

Page 41: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 41

Problem: Browser-Fenster

• Browser-Fenster erfordert einen Desktop:

Problem auf Continuous-Integration-Servern

• Browser-Fenster poppt auf:

Stört ungemein bei kontinuierlichem Testen

Page 42: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 42

Ansatz: Headless Browser

• Wenn der Browser sowieso ferngesteuert wird,

braucht er eigentlich kein Fenster

Page 43: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 43

Tool: PhantomJS

• Selenium-kompatibel

• nah an Chrome und Safari

• schnell (kein Rendering)

Page 44: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 44

Praxis: PhantomJS

Page 45: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 45

Demo: PhantomJS via Selenium

Page 46: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 46

Set Up, Tear Down: Komponenten

• Server

• Web Driver

Page 47: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 47

Set Up, Tear Down: Lebenszyklus

• pro Feature

• pro Scenario

Page 48: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 48

Erfahrung: Spezifikationsproblem

• Auftraggeber möchte sich nicht festlegen

• Entwickler denken sich etwas aus

• Sauber getestete Software, die hervorragend das tut, was niemand braucht

• Overhead für die Acceptance-Tests reduzieren die Bereitschaft für Änderungen

Page 49: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 49

Erfahrung: Spezifikationsproblem

BDD funktioniert nur, wenn

sich jemand von der

Auftragsseite darauf einlässt

Page 50: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Herbstcampus 2013 – Geister, Gurken und Halbmetalle 50

Zusammenfassung

• Behavior Driven Development mit Gherkin

• Tools: • Cucumber, Cucumber-JVM, Specflow

• Selenium

• PhantomJS

• Interaktion: • Selenium-API

• XPath

• JavaScript

• Folien, Code: http://malteclasen.de/blog

• Beratung, Training: [email protected]

Page 51: Geister, Gurken und Halbmetalle - Malte Clasenmalteclasen.de/blog/wp-content/uploads/2013/09/... · Ansatz: Headless Browser •Wenn der Browser sowieso ferngesteuert wird, braucht

Vielen Dank!

Malte Clasen http://malteclasen.de