Feige sein! Testen im Java-EE-Umfeld

Preview:

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)

Citation preview

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!

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/

dirk.weil@gedoplan.de

@dirkweil

34 Feige sein! dirk.weil@gedoplan.de

Recommended