Start / Cheat Sheets

Cheat Sheet

Quarkus testen, betreiben und migrieren — Cheat Sheet

Stand: · Quarkus für Spring-Boot-Teams

QuarkusDev ServicesOIDCNative ImageMigration

Die Seite nach dem Code: Was der Test macht, was der Betrieb sieht und woran man merkt, dass eine Migration wirklich fertig ist. Maßgeblich bleibt die Doku auf quarkus.io; hier stehen die Werte und Pfade, die man im Alltag nachschlägt — und die Abnahmeliste, die Versionssprünge überlebt.

Annotationen, Konfigurationsschlüssel und Panache stehen auf dem Blatt Spring Boot nach Quarkus übertragen.

Welcher Test wofür

@QuarkusTest@QuarkusIntegrationTest
Gegenstandlaufende Anwendunggebautes Artefakt
Injektionmöglichnicht möglich
AusführungSurefireFailsafe
Nativeneinja, gegen das Native Image

Die Trennung über Surefire und Failsafe ist Pflicht — beide Arten gehören nicht in denselben Testlauf. JUnit 5 bleibt die Grundlage, REST Assured kennt den Port bereits, @TestHTTPResource und @TestHTTPEndpoint liefern URL und Pfad ohne Literale, und @TestTransaction rollt einen Test nach dem Lauf zurück.

@QuarkusTest
class RueckgabeTest {
  @InjectMock AbrechnungClient client;

  @Test void meldetRueckgabe() {
    given().body(dto).contentType(JSON)
      .when().post("/rueckgaben")
      .then().statusCode(201);
  }
}

Ein @Singleton-Bean wird nur mit @MockitoConfig(convertScopes = true) über @InjectMock ersetzt. Jedes zusätzliche Testprofil bedeutet einen weiteren Anwendungsstart — das ist der übliche Grund für eine langsame Suite.

Modul: Anwendungen mit Quarkus testen

Dev Services

Gesetzt wird nur, was wirklich abweicht — ohne Angabe wählt Quarkus ein passendes Image und verdrahtet es selbst.

quarkus.devservices.enabled=true
quarkus.devservices.timeout=60S
quarkus.datasource.devservices.image-name=postgres:17
quarkus.datasource.devservices.reuse=true
quarkus.kafka.devservices.shared=true

PostgreSQL liefert das Schema, das Flyway beim Start migriert; der Kafka-Dev-Service ersetzt den Broker; Keycloak stellt Realm und Tokens bereit. Mit shared und service-name teilen sich mehrere Anwendungen einen Container. Eine halb gesetzte Verbindungskonfiguration verhindert den Start des Dev Service — ohne Hinweis.

Modul: Dev Services statt manueller Testinfrastruktur

Absicherung: Spring Security zu Quarkus Security

Spring SecurityQuarkus Security
@PreAuthorize mit Ausdruck@RolesAllowed mit Rollenliste
@Secured@RolesAllowed
permitAll in der Chain@PermitAll
denyAll in der Chain@DenyAll
antMatchers in der Chainquarkus.http.auth.permission-Einträge

Rollenbasierte Prüfungen wirken an REST-Endpunkten und an CDI-Beans; die Zusicherung steht damit an der Methode und ist beim Lesen des Codes sichtbar. SecurityIdentity ist die eine Quelle für Benutzer, Rollen und Attribute.

quarkus.oidc.auth-server-url=https://idp/realms/pfand
quarkus.oidc.client-id=pfandkreis
quarkus.oidc.application-type=service
quarkus.oidc.token.audience=pfandkreis
quarkus.oidc.roles.role-claim-path=realm_access/roles

Getestet wird über @TestSecurity (Benutzer, Rollen, Berechtigungen ohne echten Provider), @OidcSecurity für Claims, den KeycloakTestClient für echte Tokens oder OidcWiremockTestResource aus quarkus-test-oidc-server.

Modul: Von Spring Security zu Quarkus Security

Messaging: vom Listener zum Kanal

Spring for Apache KafkaQuarkus Messaging
@KafkaListener@Incoming auf der Methode
KafkaTemplate.send@Outgoing oder Emitter
Topic in der AnnotationTopic in der Konfiguration
ConsumerRecordMessage mit Metadaten
Container-FactoryConnector smallrye-kafka

Der Kanalname im Schlüssel ist derselbe, der in @Incoming und @Outgoing steht — weicht er ab, bleibt der Consumer stumm.

kafka.bootstrap.servers=broker:9092
mp.messaging.incoming.rueckgaben.topic=pfand.rueckgabe
mp.messaging.incoming.rueckgaben.group.id=pfandkreis
mp.messaging.outgoing.buchungen.topic=pfand.buchung

Datenbankzugriffe in der Verarbeitung gehören mit @Blocking auf einen Worker-Thread.

Modul: Messaging und externe Systeme

Fehlertoleranz: Vorgabewerte, die man kennen sollte

AnnotationVorgabeBedeutung
@RetrymaxRetries 3drei Wiederholungen
@Timeout1 Sekundeharte Zeitgrenze
@Bulkheadvalue 10zehn gleichzeitige Aufrufe
@CircuitBreakerfailureRatio 0.5Hälfte der Aufrufe fehlerhaft
@CircuitBreakerrequestVolumeThreshold 20Beobachtungsfenster
@CircuitBreakerdelay 5sWartezeit bis zum Probelauf

Je Methode überschreibbar über quarkus.fault-tolerance."Klasse/methode".retry.max-retries.

@Retry(maxRetries = 2)
@Timeout(2000)
@CircuitBreaker(requestVolumeThreshold = 10)
@Fallback(fallbackMethod = "ausCache")
public Konto laden(String id) { ... }

Konto ausCache(String id) { ... }   // gleiche Signatur, sonst greift es nicht

Reaktiv und imperativ dürfen im selben Projekt stehen: Reaktive Endpunkte laufen auf wenigen I/O-Threads, blockierende auf Worker-Threads. await oder toIterable auf einem I/O-Thread schlagen bewusst fehl.

Modul: Resiliente und reaktive Anwendungen

Observability: vom Actuator zu Quarkus

Spring Boot ActuatorQuarkus
/actuator/health/q/health
/actuator/health/liveness/q/health/live
/actuator/health/readiness/q/health/ready
HealthIndicatorHealthCheck mit @Liveness oder @Readiness
Micrometer-RegistryMicrometer-Extension

Verschiebbar über quarkus.smallrye-health.root-path. Die drei Prüfungen bleiben getrennt, weil sie verschiedene Fragen beantworten: Liveness entscheidet über den Neustart des Pods, Readiness über die Zuleitung von Anfragen, Startup verschiebt den Beginn der Liveness-Prüfung beim langsamen Hochlauf. Eine Prüfung, die eine Fremddatenbank abfragt, gehört nicht in Liveness.

Modul: Observability übertragen

Container und Kubernetes

quarkus.kubernetes.namespace=pfand
quarkus.kubernetes.replicas=3
quarkus.kubernetes.image-pull-policy=always
quarkus.kubernetes.env.vars.db-host=postgres
quarkus.kubernetes.liveness-probe.period=10S
quarkus.kubernetes.readiness-probe.period=5S

quarkus.kubernetes.deploy=true rollt beim Build direkt in den Cluster aus — ein Schalter, der nicht versehentlich stehenbleiben sollte. Konfiguration und Secrets kommen aus ConfigMaps und Secrets, nie aus dem Image; Graceful Shutdown lässt laufende Anfragen zu Ende kommen.

Modul: Container und Kubernetes

JVM-Build gegen Native Build

JVM-BuildNative Build
DauerSekundenmehrere Minuten
Speicherbedarfgeringmehrere Gigabyte
Dynamik zur Laufzeitvollständignur was registriert ist
Test des ArtefaktsJVM-TestsIntegrationstests gegen das Image
./mvnw install -Dnative
quarkus build --native
./mvnw install -Dnative -Dquarkus.native.container-build=true

Seit Quarkus 3.36 ist GraalVM beziehungsweise Mandrel 25.0 die minimal offiziell unterstützte Version. Reflektiver Zugriff, dynamische Proxys und Ressourcen brauchen ausdrückliche Registrierung — was in der JVM funktioniert, kann nativ fehlen; umgekehrt nie.

Modul: JVM, AOT und Native Image vergleichen

Die Abnahmeliste

Erst wenn jede Zeile abgehakt ist, wird der alte Dienst abgeschaltet.

PrüfpunktNachweis
Fachliches VerhaltenCharacterization Tests bestehen beidseitig
API-Vertraggleiche Statuscodes und Antwortformate
Fehlerfällegleiche Formate, gleiche Codes
Securityerlaubte und verbotene Zugriffe identisch
Messaginggleiche Nachrichten, keine Doppelverarbeitung
RessourcenStartzeit, Speicher und Antwortzeit gemessen

Modul: Eine Spring-Boot-Anwendung migrieren

Werkzeuge für die Umstellung

WerkzeugAufgabe
Migration Toolkit for Applicationsregelbasierte Analyse des Bestands
OpenRewritedeterministische Codeänderungen nach Rezept
Quarkus Update ToolQuarkus-Projekt auf einen neueren Stream heben
Konveyor AIAnalyse mit Vorschlägen in der Entwicklungsumgebung

Für Quarkus-nach-Quarkus-Updates genügt quarkus update, wahlweise mit --stream. Es stützt sich auf OpenRewrite-Rezepte, und die Dokumentation sagt ausdrücklich, dass damit nur ein Teil der Migration automatisiert wird: vor dem Lauf ein eigener Zweig, nach dem Lauf Ausgabe und Diff prüfen, dann Build und Tests.

Modul: Migrationstools, KI-Unterstützung und Roadmap

Typische Fallen

  • Integrationstests landen in Surefire und starten damit die falsche Variante; Injektion in @QuarkusIntegrationTest schlägt fehl, weil der Prozess ein anderer ist.
  • reuse ist eingeschaltet und Tests beeinflussen sich über alte Daten.
  • Ohne festes Image-Tag ändert ein Testlauf unbemerkt die Datenbankversion.
  • Die CI läuft ohne Container-Laufzeit und die Suite bricht erst dort ab.
  • Die Audience wird nicht geprüft — fremde Tokens desselben Providers gelten damit.
  • Der Rollen-Claim heißt beim Provider anders als erwartet und alle Rollen fehlen.
  • Pfadregeln und Annotationen widersprechen sich, die restriktivere gewinnt unbemerkt; CORS und CSRF werden vergessen, weil sie in Spring woanders standen.
  • Beide Varianten laufen mit derselben Gruppen-Kennung und teilen sich die Nachrichten.
  • Eine Dead Letter Queue wird eingerichtet, aber niemandem zugewiesen.
  • @Retry ohne @Timeout verlängert den Ausfall, statt ihn abzufangen — und Timeout mal Wiederholungen sprengt die Zeitgrenze des Aufrufers.
  • Liveness prüft eine externe Abhängigkeit, und ein fremder Ausfall startet den Pod neu.
  • Readiness meldet bereit, bevor Flyway die Migration beendet hat.
  • Die Probes zeigen weiter auf die Actuator-Pfade und schlagen nach der Migration fehl.
  • Requests und Limits bleiben aus der Spring-Boot-Zeit stehen — der Speichervorteil bleibt damit ungenutzt.
  • Gemessen wird der Kaltstart, obwohl der Dienst wochenlang läuft.

Zum Seminar Quarkus für Spring-Boot-Teams