Date post: | 14-Apr-2017 |
Category: |
Software |
Upload: | berndmueller |
View: | 41 times |
Download: | 0 times |
— Drohnen und WARP-Antriebe —
JSF-Anwendungen mit
Arquillian Drone und
Arquillian WARP testen
,Bernd Muller, JAX 2014, 15.5.2014 2/61
Agenda
I Arquillian
I Arquillian Drone
I Arquillian WARP
,Bernd Muller, JAX 2014, 15.5.2014 3/61
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
Arquillian
,Bernd Muller, JAX 2014, 15.5.2014 5/61
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 );
}
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
”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
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
Arquillian Drone
,Bernd Muller, JAX 2014, 15.5.2014 18/61
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
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
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
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
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
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?)
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?)
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?)
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
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
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
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 ());
}
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 ());
}
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
@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
@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
@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
@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
@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
Arquillian WARP
,Bernd Muller, JAX 2014, 15.5.2014 28/61
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
@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
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
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
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
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
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
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
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
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
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
Dokumentation WARP
,Bernd Muller, JAX 2014, 15.5.2014 58/61
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
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
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
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
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
Fragen und Anmerkungen
,Bernd Muller, JAX 2014, 15.5.2014 61/61