feige sein! testen im java-ee-umfeld
DESCRIPTION
GEDOPLAN-Vortrag zum Thema Unit Tests, Multi Unit Tests und Integration Tests insbesondere für Anwendungen auf Basis der Java EE. (www.gedoplan.de)TRANSCRIPT
Feige sein!
Testen im EE-Umfeld
Dirk Weil, GEDOPLAN GmbH
Dirk Weil
GEDOPLAN GmbH, Bielefeld
Java EE seit 1998
Konzeption und
Realisierung
Vorträge
Seminare
Veröffentlichungen
2 Feige sein!
Testen
3 Feige sein!
Bei mir läuft's!
Dafür haben wir
kein Budget.
Gestern ging‘s
noch (und ich hab‘
nix gemacht).
Wir haben
keinen
Testserver
EE-Tests sind zu
aufwändig.
Testen
4 Feige sein!
Testen ist feige!
… dann lassen Sie uns feige sein!
EE-Tests
Komplexe Komponenten-Landschaft
CDI / EJB
@Inject, @Produces, @Alternative …
Plattform
@PersistenceContext, @Transactional, …
Web
@Named, @XyzScoped, @Path
5 Feige sein!
real
embedded
SE-Test
Was und wie schnell?
6 Feige sein!
Ausf
ühru
ngsz
eit
Testtiefe
Unit
Test
Multi Unit
Test
Integration
Test
Einzelklasse Komponente
Subsystem
Anwendung
Service
(inkl. Umgebung)
In-Container-Test
Beispielprojekt
7 Feige sein!
WaehrungRepository
WaehrungService
WaehrungModel
+ Views
WaehrungRestService
BestellungRepository
ArtikelRepository
ShopModel
+ Views
Artikel
Bestellung
Waehrung
Bestell-
Position
EntityManager / PU TX Manager
Unit Test
Test: Umrechnung in WaehrungService
Mock für WaehrungRepository
Toolset: JUnit, Mockito
8 Feige sein!
WaehrungRepository
WaehrungService Waehrung
Unit Test
9 Feige sein!
@Stateless @LocalBean
public class WaehrungService implements WaehrungServiceRemote
{
@Inject
WaehrungRepository waehrungRepository;
public BigDecimal getTauschkurs(String waehrungId)
{
Waehrung waehrung = waehrungRepository.findById(waehrungId);
public class WaehrungServiceUnitTest
{
@BeforeClass
public static void beforeClass()
{
repository = Mockito.mock(WaehrungRepository.class);
Mockito.when(repository.findById("USD")).thenReturn(USD);
service = new WaehrungService();
service.waehrungRepository = repository;
}
Mock-Objekt vorbereiten
„Injektion“
Multi Unit Test
Test: Umrechnung in WaehrungService
Test-DB statt Prod-DB
Toolset: JUnit, CDI-Container
alternativer Producer für EntityManager
TX-Interceptor
10 Feige sein!
WaehrungRepository
WaehrungService Waehrung
EntityManager / PU TX Manager
Multi Unit Test
Apache
DeltaSpike
Container-
Start
Kontext-
Start
Injektion
in unmanaged
Objects
public class WaehrungServiceMultiTest { private static CdiContainer cdiContainer; @BeforeClass public static void beforeClass() { cdiContainer = CdiContainerLoader.getCdiContainer(); cdiContainer.boot(); cdiContainer.getContextControl().startContexts(); } @Before public void before() { BeanProvider.injectFields(this); } @Inject WaehrungService waehrungService;
Multi Unit Test
Mocking – the CDI way
(1: Alternative)
12 Feige sein!
public class EntityManagerProducer
{
@PersistenceContext(unitName = "seminar")
@Produces
private EntityManager entityManager;
} @Alternative @Priority(1)
public class TestEntityManagerProducer
{
EntityManagerFactory emf;
@PostConstruct
private void postConstruct()
{
emf= Persistence.createEntityManagerFactory("test");
}
@Produces
EntityManager createEntityManager()
{
return emf.createEntityManager();
Mock-Objekt
nur im
Test-Classpath
Multi Unit Test
Mocking – the CDI way
(2: Interceptor)
13 Feige sein!
@Transactional
public class WaehrungRepository
{
public void persist(Waehrung entity)
{
this.entityManager.persist(entity);
@Interceptor @Priority(Interceptor.Priority.APPLICATION + 1)
@Transactional
public class TestTransactionInterceptor
{
@Inject
EntityManager entityManager;
@AroundInvoke
Object manageTX(InvocationContext invocationContext) throws Exception
{
this.entityManager.getTransaction().begin();
Mock-Objekt
nur im
Test-Classpath
Testdaten-Bereitstellung
Bulk Load
Skript, DBUnit, …
Manuell
Prod Extract, …
Anwendungs-API
EntityManager, XyzRepository
14 Feige sein!
<waehrung
id="USD">
…
>import 2013-11-07.dump
Testdaten-Bereitstellung
Daten vor dem Test löschen …
höhere Komponenten zuerst
… und neu laden
niedere Komponenten zuerst
Daten nach Test stehen lassen!
Lässt sich gut mit CDI implementieren
(Testdaten-Lader wiederum nur im Test-Classpath)
15 Feige sein!
Integration Tests
Test in Java EE Server
Test von Komponenten
inkl. der davon genutzten Schichten
Zugriff auf alle Java-EE-Ressourcen
„echte“ Persistence Unit (mit Test-DB)
„echter“ Transaction Manager
Toolset: JUnit, Arquillian, EE Server
16 Feige sein!
Arquillian
Server Lifecycle (Start/Stop)
Erstellung von Deployment Archives
Deployment (+Undeployment)
Integration mit JUnit und TestNG
Testausführung
Server: White Box Test
Client: Black Box Test
Diverse Erweiterungen
17 Feige sein!
18 Feige sein!
Arquillian
Server starten Archiv erstellen und deployen
Tests durchführen
Archiv undeployen
Server stoppen weitere
Testklasse?
Remote Mode Managed Mode
Arquillian
Container-Wahl durch Classpath
verfügbare Container:
https://docs.jboss.org/author/display/ARQ/Container+adapters
19 Feige sein!
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<scope>test</scope>
</dependency>
<profiles>
<profile>
<id>astest_wildfly-8.0-managed</id>
<dependencies>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-arquillian-container-managed</artifactId>
<scope>test</scope>
</dependency>
Arquillian
JUnit Testrunner
Deployment mit ShrinkWrap erstellen
neuen Archivnamen nutzen (z. B. UUID)
richtige Endung!
20 Feige sein!
@RunWith(Arquillian.class)
public class WaehrungServiceIntegrationTest
{
@Deployment
public static WebArchive createDeployment()
{
WebArchive archive = ShrinkWrap.create(WebArchive.class, deploymentUnitName + ".war");
archive.addClasses(…);
archive.addAsWebInfResource(…);
archive.addAsResource(…);
archive.addAsLibraries(…);
Arquillian
White Box Test
@Deployment(testable=true) (Default)
Testklasse wird mit deployt
@Inject etc. nutzbar
21 Feige sein!
@RunWith(Arquillian.class)
public class WaehrungServiceIntegrationTest
{
…
@Inject WaehrungService waehrungService;
@Test
public void testUmrechnenUSD()
{
…
BigDecimal actual = this.waehrungService.umrechnen(fremdBetrag, fremdWaehrungId);
Assert.assertEquals("Euro-Betrag", expected, actual);
Arquillian
White Box Test
Passend erstelltes Deployment
angepasste PU-Definition
22 Feige sein!
WaehrungRepository
WaehrungService Waehrung
EntityManager / PU TX Manager
Arquillian
White Box Test
23 Feige sein!
Archiv erstellen
- Benötigte Anwendungsdateien
- ggf. spezielle Testartefakte
(z.B. persistence.xml)
- Testklasse inkl. Dependencies
(automatisch per testable=true)
Testergebnisse einsammeln
Testlauf abschließen
Archiv deployen
Tests ausführen
Archiv undeployen
Client Server
Arquillian
Black Box Test
@Deployment(testable=false)
Test läuft im Client
(IDE, Jenkins, etc.)
Zugriff nur Remote
z. B. EJB per Remoting
z. B. RESTful Service via JAX-RS Client
24 Feige sein!
@RunWith(Arquillian.class)
public class WaehrungServiceRemoteIntegrationTest
{
@Deployment(testable = false)
public static WebArchive createDeployment()
{
@RunWith(Arquillian.class)
public class WaehrungRestServiceIntegrationTest
{
@Deployment(testable = false)
public static WebArchive createDeployment()
{
Arquillian
Black Box Test
25 Feige sein!
Archiv erstellen
- Benötigte Anwendungsdateien
- ggf. spezielle Testartefakte
(z.B. persistence.xml)
Tests ausführen
Testlauf abschließen
Archiv deployen
Archiv undeployen
Client Server
remote
Drone
Arquillian-Erweiterung für Selenium
Browser-Fernsteuerung
Drone übernimmt Lifecycle-Steuerung des ggf. nötigen Browsers
@Drone injiziert WebDriver (=Fernsteuerung)
Browser wählbar via arquillian.xml
Firefox, Crome, IE, HtmlUnit, PhantomJS, …
häufig per Maven-Profil
26 Feige sein!
Drone
27 Feige sein!
WaehrungRepository
WaehrungService
WaehrungModel
+ Views
EntityManager / PU TX Manager
Waehrung
28 Feige sein!
Drone
@RunWith(Arquillian.class)
public class WaehrungGuiIntegrationTest
{
…
@Drone WebDriver webDriver;
@Test
public void testUmrechnenUSD()
{
…
this.webDriver.get(serverUrlWebContext + "/demo/waehrungsrechner.xhtml");
WebElement fremdBetragField = this.webDriver.findElement(By.id("form:fremdBetrag"));
fremdBetragField.clear();
fremdBetragField.sendKeys(fremdBetrag.toString());
…
WebElement umrechnenButton = this.webDriver.findElement(By.id("form:umrechnen"));
umrechnenButton.click();
WebElement euroBetragField = this.webDriver.findElement(By.id("form:euroBetrag"));
String actual = euroBetragField.getText();
Assert.assertEquals("Eurowert", expected, actual);
Drone
Grey Box Test
Web GUI Fernbedienung wie zuvor
Abfrage des serverseitigen Zustands
z. B. mittels Remote EJB
29 Feige sein!
@RunWith(Arquillian.class)
public class ShopGuiIntegrationTest
{
@Test
public void testCreateBestellung()
{
…
this.webDriver.get(serverUrlWebContext + "/demo/waehrungsrechner.xhtml");
…
ShopGuiIntegrationTestInspectorRemote inspector
= ServiceLocator.getEjb(ShopGuiIntegrationTestInspectorRemote.class, …);
Assert.assertTrue("Bestellung nicht gespeichert",
inspector.checkBestellungExists(expected));
Arquillian Extensions
30 Feige sein!
Persistence Initialisierung, Validierung und Cleanup
der Datenbank
JaCoCo Code Coverage Monitoring
Performance Prüfung der Ausführungszeit
Warp Grey Box Testing mit Drone
Graphene Selenium-Erweiterung u. a. mit AJAX-
Unterstützung und JQuery-Selektion
…
Arquillian Experience
Mächtiges Werkzeug
Dokumentation je nach Extension sehr gut bis nicht vorhanden
Aufwändige Konfiguration
JAR-Hölle
Ausführungsgeschwindigkeit
31 Feige sein!
Links
Beispielprojekt
https://github.com/dirkweil/feige-sein
Arquillian
http://arquillian.org/
Arquillian Testing Guide
http://www.packtpub.com/arquillian-testing-guide/book
32 Feige sein!
More
Seminare zum Thema, z. B.
Java Software Testing
http://www.gedoplan-it-training.de
http://www.gedoplan-it-consulting.de
http://javaeeblog.wordpress.com/
http://expertenkreisjava.blogspot.de/
@dirkweil
34 Feige sein! [email protected]