Drohnen und WARP-Antriebe

90

Transcript of Drohnen und WARP-Antriebe

Page 1: Drohnen und WARP-Antriebe
Page 2: Drohnen und WARP-Antriebe

— Drohnen und WARP-Antriebe —

JSF-Anwendungen mit

Arquillian Drone und

Arquillian WARP testen

,Bernd Muller, JAX 2014, 15.5.2014 2/61

Page 3: Drohnen und WARP-Antriebe

Agenda

I Arquillian

I Arquillian Drone

I Arquillian WARP

,Bernd Muller, JAX 2014, 15.5.2014 3/61

Page 4: Drohnen und WARP-Antriebe

Vorstellung Referent

I Prof. Informatik (Ostfalia, HS Braunschweig/Wolfenbuttel)

I Buchautor (JSF, Seam, JPA, ...)

I Mitglied EGs JSR 344 (JSF 2.2) und JSR 338 (JPA 2.1)

I Geschaftsfuhrer PMST GmbH

I . . .

,Bernd Muller, JAX 2014, 15.5.2014 4/61

Page 5: Drohnen und WARP-Antriebe

Arquillian

,Bernd Muller, JAX 2014, 15.5.2014 5/61

Page 6: Drohnen und WARP-Antriebe

Arquillian in der Selbstdarstellung

Auszug aus http://www.arquillian.org/

I So you can rule your code. Not the bugs.

I No more mocks.

I No more container lifecycle and deployment hassles.

I Just real tests!

,Bernd Muller, JAX 2014, 15.5.2014 6/61

Page 7: Drohnen und WARP-Antriebe

Kurzbeschreibung Arquillian

I JBoss’ Test-Framework fur Tests im Container

I Dazu JUnit oder TestNG als Test-Runner

I Und Test-Enricher, um Tests im Container laufen zu lassen

I Und ShrinkWrap als Werkzeug furs Packaging/DeploymentI Prinzipieller Ablauf:

I Test wird auf Client gepacktI Test und Laufzeiterweiterung wird im Container deploytI Test wird ausgefuhrtI Testergebnisse werden an Client zuruckgegebenI Test wird undeployt

I Aktuell: Arquillian Core Version 1.1.4.Final, 31.3.2014

,Bernd Muller, JAX 2014, 15.5.2014 7/61

Page 8: Drohnen und WARP-Antriebe

Kurzbeschreibung Arquillian

I JBoss’ Test-Framework fur Tests im Container

I Dazu JUnit oder TestNG als Test-Runner

I Und Test-Enricher, um Tests im Container laufen zu lassen

I Und ShrinkWrap als Werkzeug furs Packaging/DeploymentI Prinzipieller Ablauf:

I Test wird auf Client gepacktI Test und Laufzeiterweiterung wird im Container deploytI Test wird ausgefuhrtI Testergebnisse werden an Client zuruckgegebenI Test wird undeployt

I Aktuell: Arquillian Core Version 1.1.4.Final, 31.3.2014

,Bernd Muller, JAX 2014, 15.5.2014 7/61

Page 9: Drohnen und WARP-Antriebe

Kurzbeschreibung Arquillian

I JBoss’ Test-Framework fur Tests im Container

I Dazu JUnit oder TestNG als Test-Runner

I Und Test-Enricher, um Tests im Container laufen zu lassen

I Und ShrinkWrap als Werkzeug furs Packaging/DeploymentI Prinzipieller Ablauf:

I Test wird auf Client gepacktI Test und Laufzeiterweiterung wird im Container deploytI Test wird ausgefuhrtI Testergebnisse werden an Client zuruckgegebenI Test wird undeployt

I Aktuell: Arquillian Core Version 1.1.4.Final, 31.3.2014

,Bernd Muller, JAX 2014, 15.5.2014 7/61

Page 10: Drohnen und WARP-Antriebe

Container, Deployments, Protokolle, Adapter

I 3 BetriebsmodiI Embedded ContainerI Managed ContainerI Remote Container

I ProtokolleI Local (embedded)I Servlet 2.5 / 3.0I JMX (nur JBoss)

I Container-AdapterI JBoss-AS 4,5,6,7,8I Tomcat, JettyI GlassFishI WebLogicI WebSphereI Im Zweifel: googeln und ausprobieren

,Bernd Muller, JAX 2014, 15.5.2014 8/61

Page 11: Drohnen und WARP-Antriebe

Container, Deployments, Protokolle, Adapter

I 3 BetriebsmodiI Embedded ContainerI Managed ContainerI Remote Container

I ProtokolleI Local (embedded)I Servlet 2.5 / 3.0I JMX (nur JBoss)

I Container-AdapterI JBoss-AS 4,5,6,7,8I Tomcat, JettyI GlassFishI WebLogicI WebSphereI Im Zweifel: googeln und ausprobieren

,Bernd Muller, JAX 2014, 15.5.2014 8/61

Page 12: Drohnen und WARP-Antriebe

Container, Deployments, Protokolle, Adapter

I 3 BetriebsmodiI Embedded ContainerI Managed ContainerI Remote Container

I ProtokolleI Local (embedded)I Servlet 2.5 / 3.0I JMX (nur JBoss)

I Container-AdapterI JBoss-AS 4,5,6,7,8I Tomcat, JettyI GlassFishI WebLogicI WebSphereI Im Zweifel: googeln und ausprobieren

,Bernd Muller, JAX 2014, 15.5.2014 8/61

Page 13: Drohnen und WARP-Antriebe

Beispiel: Test-Runner und Deployment

@RunWith(Arquillian.class)

public class CustomerServiceTest {

@Deployment

public static Archive <?> createTestArchive () {

return ShrinkWrap.create(WebArchive.class , "test.war")

.addClasses(Customer.class , CustomerService.class)

.addAsResource("META -INF/persistence.xml")

.addAsWebInfResource(

new File("src/main/webapp/WEB -INF/beans.xml"));

}

...

,Bernd Muller, JAX 2014, 15.5.2014 9/61

Page 14: Drohnen und WARP-Antriebe

Beispiel: Test-Runner und Deployment

@RunWith(Arquillian.class)

public class CustomerServiceTest {

@Deployment

public static Archive <?> createTestArchive () {

return ShrinkWrap.create(WebArchive.class , "test.war")

.addClasses(Customer.class , CustomerService.class)

.addAsResource("META -INF/persistence.xml")

.addAsWebInfResource(

new File("src/main/webapp/WEB -INF/beans.xml"));

}

...

,Bernd Muller, JAX 2014, 15.5.2014 9/61

Page 15: Drohnen und WARP-Antriebe

ShrinkWrap

I Werkzeug zur Erstellung/Manipulation von Java-Archiven(JARs, WARs, EARs)

I Fruher eigenstandiges JBoss-Projekt, jetztArquillian-Teilprojekt

I Fluent-API zum Erstellen eines Archives,fur sogenannte Micro Deployment,im Beispiel zwei Klassen und Deployment-Descriptoren

,Bernd Muller, JAX 2014, 15.5.2014 10/61

Page 16: Drohnen und WARP-Antriebe

Verbesserungspotenzial

@Deployment

public static Archive <?> createTestArchive () {

return ShrinkWrap.create(WebArchive.class ,

generatedName)

.addPackage(true , Customer.class.getPackage ())

.addAsResource("META -INF/test -persistence.xml",

"META -INF/persistence.xml")

.addAsWebInfResource(EmptyAsset.INSTANCE , "beans.xml");

}

I Generierter Deployment-NameI Ganzes Package, optional mit Sub-PackagesI Spezieller Deployment-Descriptor fur Test (JPA, CDI, . . . )I Leerer Deployment-Descriptor, auch String-Asset

,Bernd Muller, JAX 2014, 15.5.2014 11/61

Page 17: Drohnen und WARP-Antriebe

Verbesserungspotenzial

@Deployment

public static Archive <?> createTestArchive () {

return ShrinkWrap.create(WebArchive.class ,

generatedName)

.addPackage(true , Customer.class.getPackage ())

.addAsResource("META -INF/test -persistence.xml",

"META -INF/persistence.xml")

.addAsWebInfResource(EmptyAsset.INSTANCE , "beans.xml");

}

I Generierter Deployment-NameI Ganzes Package, optional mit Sub-PackagesI Spezieller Deployment-Descriptor fur Test (JPA, CDI, . . . )I Leerer Deployment-Descriptor, auch String-Asset

,Bernd Muller, JAX 2014, 15.5.2014 11/61

Page 18: Drohnen und WARP-Antriebe

Verbesserungspotenzial

@Deployment

public static Archive <?> createTestArchive () {

return ShrinkWrap.create(WebArchive.class ,

generatedName)

.addPackage(true , Customer.class.getPackage ())

.addAsResource("META -INF/test -persistence.xml",

"META -INF/persistence.xml")

.addAsWebInfResource(EmptyAsset.INSTANCE , "beans.xml");

}

I Generierter Deployment-NameI Ganzes Package, optional mit Sub-PackagesI Spezieller Deployment-Descriptor fur Test (JPA, CDI, . . . )I Leerer Deployment-Descriptor, auch String-Asset

,Bernd Muller, JAX 2014, 15.5.2014 11/61

Page 19: Drohnen und WARP-Antriebe

Verbesserungspotenzial

@Deployment

public static Archive <?> createTestArchive () {

return ShrinkWrap.create(WebArchive.class ,

generatedName)

.addPackage(true , Customer.class.getPackage ())

.addAsResource("META -INF/test -persistence.xml",

"META -INF/persistence.xml")

.addAsWebInfResource(EmptyAsset.INSTANCE , "beans.xml");

}

I Generierter Deployment-NameI Ganzes Package, optional mit Sub-PackagesI Spezieller Deployment-Descriptor fur Test (JPA, CDI, . . . )I Leerer Deployment-Descriptor, auch String-Asset

,Bernd Muller, JAX 2014, 15.5.2014 11/61

Page 20: Drohnen und WARP-Antriebe

Verbesserungspotenzial

@Deployment

public static Archive <?> createTestArchive () {

return ShrinkWrap.create(WebArchive.class ,

generatedName)

.addPackage(true , Customer.class.getPackage ())

.addAsResource("META -INF/test -persistence.xml",

"META -INF/persistence.xml")

.addAsWebInfResource(EmptyAsset.INSTANCE , "beans.xml");

}

I Generierter Deployment-NameI Ganzes Package, optional mit Sub-PackagesI Spezieller Deployment-Descriptor fur Test (JPA, CDI, . . . )I Leerer Deployment-Descriptor, auch String-Asset

,Bernd Muller, JAX 2014, 15.5.2014 11/61

Page 21: Drohnen und WARP-Antriebe

Beispiel: Zu testende EJB

@Stateless

public class CustomerService {

@PersistenceContext

EntityManager em;

public void persist(Customer customer) {

em.persist(customer );

}

public long getNumberOfCustomers () {

return em.createNamedQuery(

"Customer.getNumberOfCustomers",

Long.class ). getSingleResult ();

}

}

,Bernd Muller, JAX 2014, 15.5.2014 12/61

Page 22: Drohnen und WARP-Antriebe

Beispiel: Der Test

@RunWith(Arquillian.class)

public class CustomerServiceTest {

@Deployment

public static Archive <?> createTestArchive () { ...}

@Inject

CustomerService customerService;

@Test

public void testPersist () {

Customer customer = new Customer("Firstname", "Lastname", "Email", "Password");

assertNull(customer.getId ()); // ueberfluessigcustomerService.persist(customer );

assertNotNull(customer.getId ());

}

,Bernd Muller, JAX 2014, 15.5.2014 13/61

Page 23: Drohnen und WARP-Antriebe

Beispiel: Alternativer Test

@Test

public void testPersist2 () {

Customer customer = new Customer("Firstname", "Lastname", "Email", "Password");

Long before = customerService.getNumberOfCustomers ();

customerService.persist(customer );

Long after= customerService.getNumberOfCustomers ();

assertTrue("Muss ein Customer mehr sein",

before + 1 == after );

}

,Bernd Muller, JAX 2014, 15.5.2014 14/61

Page 24: Drohnen und WARP-Antriebe

Beispiel: 3. Alternative mit Persistenzkontext

@RunWith(Arquillian.class)

public class CustomerServiceTest {

@PersistenceContext

EntityManager em;

@Test

public void testPersist3 () {

Customer customer = new Customer("Firstname", "Lastname", "Email", "Password");

Long before = em.createNamedQuery(

"Customer.getNumberOfCustomers", Long.class)

.getSingleResult ();

customerService.persist(customer );

Long after= em.createNamedQuery(

"Customer.getNumberOfCustomers", Long.class)

.getSingleResult ();

assertTrue("Muss ein Customer mehr sein",

before + 1 == after );

}

Page 25: Drohnen und WARP-Antriebe

Beispiel: 3. Alternative mit Persistenzkontext

@RunWith(Arquillian.class)

public class CustomerServiceTest {

@PersistenceContext

EntityManager em;

@Test

public void testPersist3 () {

Customer customer = new Customer("Firstname", "Lastname", "Email", "Password");

Long before = em.createNamedQuery(

"Customer.getNumberOfCustomers", Long.class)

.getSingleResult ();

customerService.persist(customer );

Long after= em.createNamedQuery(

"Customer.getNumberOfCustomers", Long.class)

.getSingleResult ();

assertTrue("Muss ein Customer mehr sein",

before + 1 == after );

}

Existiert, da Test in Container

Page 26: Drohnen und WARP-Antriebe

”Richtige Anwendungen“: Multiple Deployments (Doku)

@Deployment(name = "dep1", order = 1)

public static WebArchive createDep1 () { ... }

@Deployment(name = "dep2", order = 2)

public static WebArchive createDep2 () { ... }

@Test @OperateOnDeployment("dep1")

public void testRunningInDep1 () { ... }

@Test @OperateOnDeployment("dep2")

public void testRunningInDep2 () { ... }

,Bernd Muller, JAX 2014, 15.5.2014 16/61

Page 27: Drohnen und WARP-Antriebe

Erweiterbarkeit

I Wie alle modernen Systeme/Frameworks besitzt Arquillian einService Provider Interface

I Aktuelle Erweiterungen:I Drone (Web-UI)I WARP (JSF)I PersistenceI PerformanceI Graphene (UI, AJAX)I Seam 2

,Bernd Muller, JAX 2014, 15.5.2014 17/61

Page 28: Drohnen und WARP-Antriebe

Arquillian Drone

,Bernd Muller, JAX 2014, 15.5.2014 18/61

Page 29: Drohnen und WARP-Antriebe

Arquillian Drone

I Arquillian-Erweiterung fur funktionale TestsI Basiert auf SeleniumI Selenium-Slogan: Selenium automates browsersI WebDriver: Moglichkeit zur Browser-Steuerung, u.a. mit Java

APII WebDriver API das einzig neu zu lernendeI Anderung des prinzipiellen Ablaufs: Tests auf ClientI Kombination Client und Server moglichI Version aktuell: 1.3.0.FinalI Version 2.0.0.Alpha1 mit uberarbeitetem/entschlacktem API

in Entwicklung (von uns nicht verwendet)

,Bernd Muller, JAX 2014, 15.5.2014 19/61

Page 30: Drohnen und WARP-Antriebe

Die notigen Erweiterungen/Anderungen

@RunWith(Arquillian.class)

public class DroneUsedTest {

@ArquillianResource

URL deploymentURL;

@Drone

WebDriver driver;

@Deployment(testable = false)

public static Archive <?> createTestArchive () { ...}

I @ArquillianResource: das URL des DeploymentsI @Drone: das WebDriver oder DefaultSelenium APII Keine Tests im Server

,Bernd Muller, JAX 2014, 15.5.2014 20/61

Page 31: Drohnen und WARP-Antriebe

Die notigen Erweiterungen/Anderungen

@RunWith(Arquillian.class)

public class DroneUsedTest {

@ArquillianResource

URL deploymentURL;

@Drone

WebDriver driver;

@Deployment(testable = false)

public static Archive <?> createTestArchive () { ...}

I @ArquillianResource: das URL des DeploymentsI @Drone: das WebDriver oder DefaultSelenium APII Keine Tests im Server

,Bernd Muller, JAX 2014, 15.5.2014 20/61

Page 32: Drohnen und WARP-Antriebe

Die notigen Erweiterungen/Anderungen

@RunWith(Arquillian.class)

public class DroneUsedTest {

@ArquillianResource

URL deploymentURL;

@Drone

WebDriver driver;

@Deployment(testable = false)

public static Archive <?> createTestArchive () { ...}

I @ArquillianResource: das URL des DeploymentsI @Drone: das WebDriver oder DefaultSelenium APII Keine Tests im Server

,Bernd Muller, JAX 2014, 15.5.2014 20/61

Page 33: Drohnen und WARP-Antriebe

Die notigen Erweiterungen/Anderungen

@RunWith(Arquillian.class)

public class DroneUsedTest {

@ArquillianResource

URL deploymentURL;

@Drone

WebDriver driver;

@Deployment(testable = false)

public static Archive <?> createTestArchive () { ...}

I @ArquillianResource: das URL des DeploymentsI @Drone: das WebDriver oder DefaultSelenium APII Keine Tests im Server

,Bernd Muller, JAX 2014, 15.5.2014 20/61

Page 34: Drohnen und WARP-Antriebe

Falls nicht nur Client-Tests

@RunWith(Arquillian.class)

public class DroneUsedTest {

@Drone

WebDriver driver;

@Deployment

public static Archive <?> createTestArchive () { ... }

@Test

@RunAsClient

@InSequence (1)

public void createCustomer(@ArquillianResource URL url) { ... }

@Test // im Server@InSequence (2)

public void countCustomers () { ... }

I Auch Tests im Server moglich, daher Unterscheidung

I Reihenfolge der Tests (Widerspruch zur reinen Lehre?)

Page 35: Drohnen und WARP-Antriebe

Falls nicht nur Client-Tests

@RunWith(Arquillian.class)

public class DroneUsedTest {

@Drone

WebDriver driver;

@Deployment

public static Archive <?> createTestArchive () { ... }

@Test

@RunAsClient

@InSequence (1)

public void createCustomer(@ArquillianResource URL url) { ... }

@Test // im Server@InSequence (2)

public void countCustomers () { ... }

I Auch Tests im Server moglich, daher Unterscheidung

I Reihenfolge der Tests (Widerspruch zur reinen Lehre?)

Page 36: Drohnen und WARP-Antriebe

Falls nicht nur Client-Tests

@RunWith(Arquillian.class)

public class DroneUsedTest {

@Drone

WebDriver driver;

@Deployment

public static Archive <?> createTestArchive () { ... }

@Test

@RunAsClient

@InSequence (1)

public void createCustomer(@ArquillianResource URL url) { ... }

@Test // im Server@InSequence (2)

public void countCustomers () { ... }

I Auch Tests im Server moglich, daher Unterscheidung

I Reihenfolge der Tests (Widerspruch zur reinen Lehre?)

Page 37: Drohnen und WARP-Antriebe

Versteht Arquillian Maven’s POM ?

I Nein, aber ShrinkWrap

I Es gibt einen ShrinkWrap-Resolver, der Maven und Gradleunterstutzt

I Jetzt Beispiel:I JSF-Seite zur KundenneuanlageI Navigation zu Kundenanlage-Erfolgreich-Seite, falls kein FehlerI Test, ob Navigation funktioniertI Test, ob Daten im Server

,Bernd Muller, JAX 2014, 15.5.2014 22/61

Page 38: Drohnen und WARP-Antriebe

Versteht Arquillian Maven’s POM ?

I Nein, aber ShrinkWrap

I Es gibt einen ShrinkWrap-Resolver, der Maven und Gradleunterstutzt

I Jetzt Beispiel:I JSF-Seite zur KundenneuanlageI Navigation zu Kundenanlage-Erfolgreich-Seite, falls kein FehlerI Test, ob Navigation funktioniertI Test, ob Daten im Server

,Bernd Muller, JAX 2014, 15.5.2014 22/61

Page 39: Drohnen und WARP-Antriebe

Das (fast) komplette Beispiel (Teil 1)

@RunWith(Arquillian.class)

public class CreateCustomerTest {

private static final String

WEBAPP_SRC = "src/main/webapp";

@Drone

WebDriver driver;

@Inject

CustomerRepository customerRepository;

,Bernd Muller, JAX 2014, 15.5.2014 23/61

Page 40: Drohnen und WARP-Antriebe

Das (fast) komplette Beispiel (Teil 2)

@Deployment

public static Archive <?> createTestArchive () {

MavenDependencyResolver resolver =

DependencyResolvers.use(MavenDependencyResolver.class)

.loadMetadataFromPom("pom.xml");

return ShrinkWrap.create(WebArchive.class , "test.war")

.addPackages (...). addClasses (...)

.addAsResource("META -INF/test -persistence.xml",

"META -INF/persistence.xml")

.addAsWebInfResource("test -ds.xml")

.addAsWebResource(new File(WEBAPP_SRC ,

"create.xhtml"))

.addAsWebResource(new File(WEBAPP_SRC ,

"customer -created.xhtml"))

.addAsWebInfResource(EmptyAsset.INSTANCE , "beans.xml")

.addAsWebInfResource(

new StringAsset("<faces -config version =\"2.0\"/ >"),

"faces -config.xml")

.addAsLibraries(

resolver.artifact("org.seleniumhq.selenium:selenium -java")

.resolveAsFiles ());

}

Page 41: Drohnen und WARP-Antriebe

Das (fast) komplette Beispiel (Teil 3)

@Test

@RunAsClient

@InSequence (1)

public void createCustomer(@ArquillianResource URL url) {

driver.get(url + "create.jsf");

driver.findElement(By.id("create:firstname"))

.sendKeys("firstname");

driver.findElement(By.id("create:lastname"))

.sendKeys("lastname");

driver.findElement(By.id("create:dob"))

.sendKeys("1.1.2000");

driver.findElement(By.id("create:email"))

.sendKeys("email");

driver.findElement(By.id("create:password"))

.sendKeys("password");

driver.findElement(By.id("create:create")). click ();

assertTrue(driver.findElement(

By.xpath("//body[contains(text(), ’Kunde angelegt ’)]"))

.isDisplayed ());

}

Page 42: Drohnen und WARP-Antriebe

Das (fast) komplette Beispiel (Teil 4)

@Test // im Server@InSequence (2)

public void countCustomers () {

assertEquals (1, customerRepository.getCustomers (). size ());

}

,Bernd Muller, JAX 2014, 15.5.2014 26/61

Page 43: Drohnen und WARP-Antriebe

@Drone im Detail

I Injection in KlasseI Class-Based Life-CycleI Analog zu @BeforeClass / @AfterClass

I Injection in MethodeI Method-based Life-CycleI Analog zu @Before / @After

I Mehrere Injektionen uber Qualifier moglich

I Verschiedene Driver: WebDriver, FirefoxDriver, ChromeDriver,InternetExplorerDriver, SafariDriver

I Durch Selenium Unterstutzung von XPath-Ausdrucken inTest-Asserts

,Bernd Muller, JAX 2014, 15.5.2014 27/61

Page 44: Drohnen und WARP-Antriebe

@Drone im Detail

I Injection in KlasseI Class-Based Life-CycleI Analog zu @BeforeClass / @AfterClass

I Injection in MethodeI Method-based Life-CycleI Analog zu @Before / @After

I Mehrere Injektionen uber Qualifier moglich

I Verschiedene Driver: WebDriver, FirefoxDriver, ChromeDriver,InternetExplorerDriver, SafariDriver

I Durch Selenium Unterstutzung von XPath-Ausdrucken inTest-Asserts

,Bernd Muller, JAX 2014, 15.5.2014 27/61

Page 45: Drohnen und WARP-Antriebe

@Drone im Detail

I Injection in KlasseI Class-Based Life-CycleI Analog zu @BeforeClass / @AfterClass

I Injection in MethodeI Method-based Life-CycleI Analog zu @Before / @After

I Mehrere Injektionen uber Qualifier moglich

I Verschiedene Driver: WebDriver, FirefoxDriver, ChromeDriver,InternetExplorerDriver, SafariDriver

I Durch Selenium Unterstutzung von XPath-Ausdrucken inTest-Asserts

,Bernd Muller, JAX 2014, 15.5.2014 27/61

Page 46: Drohnen und WARP-Antriebe

@Drone im Detail

I Injection in KlasseI Class-Based Life-CycleI Analog zu @BeforeClass / @AfterClass

I Injection in MethodeI Method-based Life-CycleI Analog zu @Before / @After

I Mehrere Injektionen uber Qualifier moglich

I Verschiedene Driver: WebDriver, FirefoxDriver, ChromeDriver,InternetExplorerDriver, SafariDriver

I Durch Selenium Unterstutzung von XPath-Ausdrucken inTest-Asserts

,Bernd Muller, JAX 2014, 15.5.2014 27/61

Page 47: Drohnen und WARP-Antriebe

@Drone im Detail

I Injection in KlasseI Class-Based Life-CycleI Analog zu @BeforeClass / @AfterClass

I Injection in MethodeI Method-based Life-CycleI Analog zu @Before / @After

I Mehrere Injektionen uber Qualifier moglich

I Verschiedene Driver: WebDriver, FirefoxDriver, ChromeDriver,InternetExplorerDriver, SafariDriver

I Durch Selenium Unterstutzung von XPath-Ausdrucken inTest-Asserts

,Bernd Muller, JAX 2014, 15.5.2014 27/61

Page 48: Drohnen und WARP-Antriebe

Arquillian WARP

,Bernd Muller, JAX 2014, 15.5.2014 28/61

Page 49: Drohnen und WARP-Antriebe

Arquillian Warp

I Client-seitige Tests mit Zusicherungen/Prufung vonServer-seitiger Logik und Zustand

I Keine weiteren Bibliotheken, wie etwa Selenium, benotigt

I Kein UI benotigt (Headless)

I Speziell fur Servlets und Servlet-basierte Systeme gemacht

I Im Augenblick nur Servlets direkt und JSF 2 unterstutzt

I Arquillian Spring Framework Extension enthalt Warp SpringMVC Extension (nicht betrachtet)

I Offizieller Nachfolger von JSFUnit

I Aktuell: Version 1.0.0.Alpha7, 11.3.2014

,Bernd Muller, JAX 2014, 15.5.2014 29/61

Page 50: Drohnen und WARP-Antriebe

Die prinzipielle Idee hinter WARP

I Initiierung eines HTTP-Requests auf Client-Seite z.B. mitWebDriver

I Im selben Request-Zyklus Ausfuhren von Server-seitigen Testsim Container

I Dazu:I Initiieren des Request uber Interface ActivityI Inspizieren des Server-Zustands uber Klasse InspectionI Aktivity mit perform()-MethodeI Inspection mit JUnit/TestNG-Tests zum

”richtigen“ Zeitpunkt

I optional: Gruppieren und Observieren von Requests

,Bernd Muller, JAX 2014, 15.5.2014 30/61

Page 51: Drohnen und WARP-Antriebe

Die notigen Erweiterungen/Anderungen

I @WarpTest fur Test-Klasse

I @RunAsClient fur Test-Klasse

I @BeforeServlet, @AfterServlet fur Servlet-Tests

I @BeforePhase, @AfterPhase (6 Phasen) fur JSF-Tests

I Im Folgenden 3 BeispieleI Existieren die richtigen Beans?I ValidierungI Navigation

,Bernd Muller, JAX 2014, 15.5.2014 31/61

Page 52: Drohnen und WARP-Antriebe

Die notigen Erweiterungen/Anderungen

I @WarpTest fur Test-Klasse

I @RunAsClient fur Test-Klasse

I @BeforeServlet, @AfterServlet fur Servlet-Tests

I @BeforePhase, @AfterPhase (6 Phasen) fur JSF-Tests

I Im Folgenden 3 BeispieleI Existieren die richtigen Beans?I ValidierungI Navigation

,Bernd Muller, JAX 2014, 15.5.2014 31/61

Page 53: Drohnen und WARP-Antriebe

Die notigen Erweiterungen/Anderungen

I @WarpTest fur Test-Klasse

I @RunAsClient fur Test-Klasse

I @BeforeServlet, @AfterServlet fur Servlet-Tests

I @BeforePhase, @AfterPhase (6 Phasen) fur JSF-Tests

I Im Folgenden 3 BeispieleI Existieren die richtigen Beans?I ValidierungI Navigation

,Bernd Muller, JAX 2014, 15.5.2014 31/61

Page 54: Drohnen und WARP-Antriebe

Die notigen Erweiterungen/Anderungen

I @WarpTest fur Test-Klasse

I @RunAsClient fur Test-Klasse

I @BeforeServlet, @AfterServlet fur Servlet-Tests

I @BeforePhase, @AfterPhase (6 Phasen) fur JSF-Tests

I Im Folgenden 3 BeispieleI Existieren die richtigen Beans?I ValidierungI Navigation

,Bernd Muller, JAX 2014, 15.5.2014 31/61

Page 55: Drohnen und WARP-Antriebe

1. Beispiel: Existieren die richtigen Beans?

I Weld-Manual: Einloggen uber JSF-View-Bean

I Prufen auf Benutzername/Passwort uber EJB

I CDI-produzierte Customer-Instanz im Session-Scope

I Die Existenz dieser Instanz soll uberpruft werden

,Bernd Muller, JAX 2014, 15.5.2014 32/61

Page 56: Drohnen und WARP-Antriebe

JSF Login-Bean

@Named

@SessionScoped

public class Login implements Serializable {

@Inject

Credentials credentials;

@Inject

CustomerService customerService;

private Customer loggedInCustomer;

...

,Bernd Muller, JAX 2014, 15.5.2014 33/61

Page 57: Drohnen und WARP-Antriebe

JSF Login-Bean (cont’d)

public void login () {

loggedInCustomer = customerService.getCustomer (...

if (loggedInCustomer == null) {

FacesMessage message = new FacesMessage("Falsche ...");

FacesContext.getCurrentInstance (). addMessage (...);

}

}

@Produces @LoggedIn

Customer getloggedInCustomer () {

return loggedInCustomer;

}

public void logout () {

loggedInCustomer = null;

}

,Bernd Muller, JAX 2014, 15.5.2014 34/61

Page 58: Drohnen und WARP-Antriebe

Logged-In-Test

@RunWith(Arquillian.class)

@WarpTest

@RunAsClient

public class LoggedInTest {

@Deployment

public static Archive <?> createTestArchive () {

// nichts besonderes}

@ArquillianResource

URL contextPath;

@Drone

WebDriver browser;

,Bernd Muller, JAX 2014, 15.5.2014 35/61

Page 59: Drohnen und WARP-Antriebe

Logged-In-Test (cont’d)

@Test

public void testLoggedIn () {

browser.navigate ().to(contextPath + "login.jsf");

browser.findElement(By.id("form:email")). sendKeys(EMAIL );

browser.findElement(By.id("form:password")). sendKeys(PASSWORD );

Warp

.initiate(new Activity () {

public void perform () {

browser.findElement(By.id("form:login")). click ();

}})

.inspect(new Inspection () {

private static final long serialVersionUID = 1L;

@Inject

@LoggedIn

Customer customer;,

Bernd Muller, JAX 2014, 15.5.2014 36/61

Page 60: Drohnen und WARP-Antriebe

Struktur zur Verdeutlichung

Warp

.initiate(new Activity () {

...

})

.inspect(new Inspection () {

...

})

I Aktivitat auf dem Client initiieren

I Inspektion auf dem Server ausfuhren

,Bernd Muller, JAX 2014, 15.5.2014 37/61

Page 61: Drohnen und WARP-Antriebe

Logged-In-Test (cont’d)

Warp

.initiate(new Activity () {...} )

.inspect(new Inspection () {

private static final long serialVersionUID = 1L;

@Inject

@LoggedIn

Customer customer;

@BeforePhase(Phase.INVOKE_APPLICATION)

public void noCustomerExists () {

Assert.assertNull(

"LoggedIn Customer darf nicht existieren",

customer );

}

...

,Bernd Muller, JAX 2014, 15.5.2014 38/61

Page 62: Drohnen und WARP-Antriebe

Logged-In-Test (cont’d)

@BeforePhase(Phase.INVOKE_APPLICATION)

public void noCustomerExists () {

Assert.assertNull(

"LoggedIn Customer darf nicht existieren",

customer );

}

@AfterPhase(Phase.INVOKE_APPLICATION)

public void loggedInCustomerExists () {

Assert.assertNotNull(

"LoggedIn Customer muss existieren",

customer );

Assert.assertEquals("Falsche E-Mail",

EMAIL , customer.getEmail ());

Assert.assertEquals("Falsches Passwort",

PASSWORD , customer.getPassword ());

},

Bernd Muller, JAX 2014, 15.5.2014 39/61

Page 63: Drohnen und WARP-Antriebe

Logged-In-Test (cont’d)

@BeforePhase(Phase.INVOKE_APPLICATION)

public void noCustomerExists () {

Assert.assertNull(

"LoggedIn Customer darf nicht existieren",

customer );

}

@AfterPhase(Phase.INVOKE_APPLICATION)

public void loggedInCustomerExists () {

Assert.assertNotNull(

"LoggedIn Customer muss existieren",

customer );

Assert.assertEquals("Falsche E-Mail",

EMAIL , customer.getEmail ());

Assert.assertEquals("Falsches Passwort",

PASSWORD , customer.getPassword ());

},

Bernd Muller, JAX 2014, 15.5.2014 39/61

Page 64: Drohnen und WARP-Antriebe

Logged-In-Test (cont’d)

@BeforePhase(Phase.INVOKE_APPLICATION)

public void noCustomerExists () {

Assert.assertNull(

"LoggedIn Customer darf nicht existieren",

customer );

}

@AfterPhase(Phase.INVOKE_APPLICATION)

public void loggedInCustomerExists () {

Assert.assertNotNull(

"LoggedIn Customer muss existieren",

customer );

Assert.assertEquals("Falsche E-Mail",

EMAIL , customer.getEmail ());

Assert.assertEquals("Falsches Passwort",

PASSWORD , customer.getPassword ());

},

Bernd Muller, JAX 2014, 15.5.2014 39/61

Page 65: Drohnen und WARP-Antriebe

2. Beispiel: Validierung

I Credentials werden falsch eingegeben (zu kurz)

I JSF erzeugt in Phase 3 FacesMessage-Instanzen

I Prufen vor und nach Phase 3, ob Messages existieren odernicht

,Bernd Muller, JAX 2014, 15.5.2014 40/61

Page 66: Drohnen und WARP-Antriebe

Klasse Credentials

@Named

@RequestScoped

public class Credentials {

@NotNull

@Size(min = 6, max = 30)

private String email;

@NotNull

@Size(min = 6, max = 20)

private String password;

...

I Deutsche Fehlermeldung fur @Size: ”muss zwischen 6 ...

,Bernd Muller, JAX 2014, 15.5.2014 41/61

Page 67: Drohnen und WARP-Antriebe

Validierungstest

@RunWith(Arquillian.class)

@WarpTest

@RunAsClient

public class LoggedInValidationFailedTest {

// nichts besonderes im Deployment

@ArquillianResource

URL contextPath;

@Drone

WebDriver browser;

...

,Bernd Muller, JAX 2014, 15.5.2014 42/61

Page 68: Drohnen und WARP-Antriebe

Validierungstest (cont’d)

@Test

public void testValidation () {

browser.navigate ().to(contextPath + "login.jsf");

browser.findElement(By.id("form:email")). sendKeys("short");

browser.findElement(By.id("form:password")). sendKeys("short");

Warp

.initiate(new Activity () {

public void perform () {

browser.findElement(By.id("form:login")). click ();

}})

.inspect(new Inspection () {

private static final long serialVersionUID = 1L;

...

,Bernd Muller, JAX 2014, 15.5.2014 43/61

Page 69: Drohnen und WARP-Antriebe

Validierungstest (cont’d)

Warp

.initiate(new Activity () {...} )

.inspect(new Inspection () {

private static final long serialVersionUID = 1L;

@ArquillianResource

FacesContext facesContext;

@BeforePhase(Phase.PROCESS_VALIDATIONS)

public void noFacesMessage () {

Assert.assertEquals("Anzahl Fehler muss 0 sein",

0, facesContext.getMessageList (). size ());

}

...

,Bernd Muller, JAX 2014, 15.5.2014 44/61

Page 70: Drohnen und WARP-Antriebe

Validierungstest (cont’d)

Warp

.initiate(new Activity () {...} )

.inspect(new Inspection () {

private static final long serialVersionUID = 1L;

@ArquillianResource

FacesContext facesContext;

@BeforePhase(Phase.PROCESS_VALIDATIONS)

public void noFacesMessage () {

Assert.assertEquals("Anzahl Fehler muss 0 sein",

0, facesContext.getMessageList (). size ());

}

...

FacesContext injizierbar!

,Bernd Muller, JAX 2014, 15.5.2014 44/61

Page 71: Drohnen und WARP-Antriebe

Validierungstest (cont’d)

.inspect(new Inspection () {

...

@ArquillianResource

FacesContext facesContext;

@AfterPhase(Phase.PROCESS_VALIDATIONS)

public void facesMessagesExist () {

List <FacesMessage > messages =

facesContext.getMessageList ();

Assert.assertTrue(

facesContext.getMessageList (). size() != 0);

for (FacesMessage facesMessage : messages) {

Assert.assertTrue("Falsche Fehlermeldung",

facesMessage.getSummary ()

.startsWith("muss zwischen 6"));

}

},

Bernd Muller, JAX 2014, 15.5.2014 45/61

Page 72: Drohnen und WARP-Antriebe

3. Beispiel: Navigation mit Redirect

I Einfache JSF-Seiten mit Navigation und Redirect

I Noch nicht erwahntes WARP-Feature: Gruppieren vonInspektionen auf Server basierend auf Request-Folge

I Prufen der View-Id nach Restore-View-Phase

,Bernd Muller, JAX 2014, 15.5.2014 46/61

Page 73: Drohnen und WARP-Antriebe

JSF-Seiten mit Navigation

I redirect-source.xhtml

<h:form id="form">

Seite verwendet #{ redirectSourceBean.name} <br />

<h:commandButton id="redirect"

action="redirect-target?faces-redirect=true"

value="Redirect to ’redirect-target.jsf ’" />

</h:form >

I redirect-target.xhtml

<h:form id="form">

Seite verwendet #{ redirectTargetBean.name} />

</h:form >

,Bernd Muller, JAX 2014, 15.5.2014 47/61

Page 74: Drohnen und WARP-Antriebe

@RunWith(Arquillian.class)

@WarpTest

@RunAsClient

public class RedirectTest {

@Deployment

public static Archive <?> createTestArchive () {

return ShrinkWrap.create(WebArchive.class ,

"redirect -test.war")

...

.addAsWebResource(new File(WEBAPP_SRC ,

"redirect -source.xhtml"))

.addAsWebResource(new File(WEBAPP_SRC ,

"redirect -target.xhtml"))

...

}

@ArquillianResource URL contextPath;

@Drone WebDriver browser;

...,

Bernd Muller, JAX 2014, 15.5.2014 48/61

Page 75: Drohnen und WARP-Antriebe

Navigationstest (cont’d)

@Test

public void testRedirect () {

browser.navigate ().to(contextPath + "redirect -source.jsf");

Warp

.initiate(new Activity () {

public void perform () {

browser.findElement(By.id("form:redirect")). click ();

}})

.group("redirect -source.jsf")

.observe(HttpFilters.request (). index (1))

.inspect(new Inspection () {

private static final long serialVersionUID = 1L;

@AfterPhase(Phase.RESTORE_VIEW)

...,

Bernd Muller, JAX 2014, 15.5.2014 49/61

Page 76: Drohnen und WARP-Antriebe

Struktur zur Verdeutlichung

Warp

.initiate(new Activity () { ... }})

.group("redirect -source.jsf") // nur Doku.observe(HttpFilters.request (). index (1))

.inspect(new Inspection () { ... })

.group("redirect -target.jsf") // nur Doku.observe(HttpFilters.request (). index (2))

.inspect(new Inspection () { ... })

.execute ();

Assert.assertTrue(browser.getCurrentUrl ()

.contains("redirect -target.jsf"));

,Bernd Muller, JAX 2014, 15.5.2014 50/61

Page 77: Drohnen und WARP-Antriebe

Navigationstest (cont’d)

.group("redirect -source.jsf")

.observe(HttpFilters.request (). index (1))

.inspect(new Inspection () {

private static final long serialVersionUID = 1L;

@AfterPhase(Phase.RESTORE_VIEW)

public void verifyPage(@ArquillianResource

FacesContext facesContext) {

Assert.assertEquals("/redirect -source.xhtml",

facesContext.getViewRoot (). getViewId ());

}

})

,Bernd Muller, JAX 2014, 15.5.2014 51/61

Page 78: Drohnen und WARP-Antriebe

Navigationstest (cont’d)

.group("redirect -target.jsf")

.observe(HttpFilters.request (). index (2))

.inspect(new Inspection () {

private static final long serialVersionUID = 1L;

@AfterPhase(Phase.RESTORE_VIEW)

public void verifyPage(@ArquillianResource

FacesContext facesContext) {

Assert.assertEquals("/redirect -target.xhtml",

facesContext.getViewRoot (). getViewId ());

}

})

.execute ();

Assert.assertTrue(browser.getCurrentUrl ()

.contains("redirect -target.jsf"));

,Bernd Muller, JAX 2014, 15.5.2014 52/61

Page 79: Drohnen und WARP-Antriebe

Auswahl des richtigen Request

I Da mehrere Request getriggert werden konnen, muss man denrichtigen (den, den man observieren will) auswahlen

I Dazu das HttpRequestFilter-Interface

I Damit spezifiziert man den zu verwendenden HttpRequest deraktuellen Gruppe

,Bernd Muller, JAX 2014, 15.5.2014 53/61

Page 80: Drohnen und WARP-Antriebe

Aus”Dokumentation“

import static org.jboss.arquillian.warp

.client.filter.http.HttpFilters.request;

// will accept only requests for HTML... group()

.observe(request ().uri(). contains(".html"))

// will accept only REST requests for JSON... group()

.observe(request (). header ()

.containsValue("Accept", "application/json"))

// will accept only POST requests... group()

.observe(request (). method (). equal(POST))

,Bernd Muller, JAX 2014, 15.5.2014 54/61

Page 81: Drohnen und WARP-Antriebe

Was kann alles injiziert werden?

I Injizierbar uber @ArquillianResourceI Servlet-Ressourcen

I ServletRequest or HttpServletRequestI ServletResponse or HttpServletResponse

,Bernd Muller, JAX 2014, 15.5.2014 55/61

Page 82: Drohnen und WARP-Antriebe

Was kann alles injiziert werden? (cont’d)

I Injizierbar uber @ArquillianResourceI JSF-Ressourcen

I FacesContextI ApplicationI ELContext, ELResolver, ExpressionFactoryI ExceptionHandlerI FlashI NavigationHandlerI PartialViewContextI RenderKitI ResourceHandlerI StateManagerI UIViewRootI ViewHandler

,Bernd Muller, JAX 2014, 15.5.2014 56/61

Page 83: Drohnen und WARP-Antriebe

Wo Licht ist, ist auch Schatten

I Sehr schlechte Doku

I Noch viele/schwere Fehler

I Scheinbar Ein-Mann-Projekt: Lukas Fryc (Project Lead)

,Bernd Muller, JAX 2014, 15.5.2014 57/61

Page 84: Drohnen und WARP-Antriebe

Dokumentation WARP

,Bernd Muller, JAX 2014, 15.5.2014 58/61

Page 85: Drohnen und WARP-Antriebe

Zusammenfassung und (personliches) Fazit

I Einerseits . . .I EAP Dokumentation enthalt kein Kapitel zu ArquillianI JBoss Dokumentation enhalt kein Kapitel zu WARP

I Andererseits . . .I EAP Quickstarts enthalten Arquillian-TestsI JB225 Schulung enthalt Arquillian- und Drone-Tests

I Arquillian und Erweiterungen sehr leistungsfahig undinteressant

I Mein Rat: fangen Sie an !

,Bernd Muller, JAX 2014, 15.5.2014 59/61

Page 86: Drohnen und WARP-Antriebe

Zusammenfassung und (personliches) Fazit

I Einerseits . . .I EAP Dokumentation enthalt kein Kapitel zu ArquillianI JBoss Dokumentation enhalt kein Kapitel zu WARP

I Andererseits . . .I EAP Quickstarts enthalten Arquillian-TestsI JB225 Schulung enthalt Arquillian- und Drone-Tests

I Arquillian und Erweiterungen sehr leistungsfahig undinteressant

I Mein Rat: fangen Sie an !

,Bernd Muller, JAX 2014, 15.5.2014 59/61

Page 87: Drohnen und WARP-Antriebe

Zusammenfassung und (personliches) Fazit

I Einerseits . . .I EAP Dokumentation enthalt kein Kapitel zu ArquillianI JBoss Dokumentation enhalt kein Kapitel zu WARP

I Andererseits . . .I EAP Quickstarts enthalten Arquillian-TestsI JB225 Schulung enthalt Arquillian- und Drone-Tests

I Arquillian und Erweiterungen sehr leistungsfahig undinteressant

I Mein Rat: fangen Sie an !

,Bernd Muller, JAX 2014, 15.5.2014 59/61

Page 88: Drohnen und WARP-Antriebe

Zusammenfassung und (personliches) Fazit

I Einerseits . . .I EAP Dokumentation enthalt kein Kapitel zu ArquillianI JBoss Dokumentation enhalt kein Kapitel zu WARP

I Andererseits . . .I EAP Quickstarts enthalten Arquillian-TestsI JB225 Schulung enthalt Arquillian- und Drone-Tests

I Arquillian und Erweiterungen sehr leistungsfahig undinteressant

I Mein Rat: fangen Sie an !

,Bernd Muller, JAX 2014, 15.5.2014 59/61

Page 89: Drohnen und WARP-Antriebe

Referenzen

I Arquillian Reference Guide

I JSF Testing

I John D. Ament. Arquillian Testing Guide. Packt Publishing,2013.

,Bernd Muller, JAX 2014, 15.5.2014 60/61

Page 90: Drohnen und WARP-Antriebe

Fragen und Anmerkungen

,Bernd Muller, JAX 2014, 15.5.2014 61/61