Quarkus-Verifizierungsschleife
Vor PRs, nach größeren Änderungen und vor dem Deployment ausführen.
Wann aktivieren
- Vor dem Öffnen eines Pull-Requests für einen Quarkus-Dienst
- Nach größeren Refactorings oder Aktualisierungen von Abhängigkeiten
- Pre-Deployment-Verifizierung für Staging oder Produktion
- Ausführen der vollständigen Build → Lint → Test → Sicherheitsscan → Native-Kompilierung-Pipeline
- Validieren, dass die Testabdeckung die Schwellwerte erfüllt (80%+)
- Testen der nativen Image-Kompatibilität
Phase 1: Build
# Maven
mvn clean verify -DskipTests
# Gradle
./gradlew clean assemble -x test
Wenn der Build fehlschlägt, anhalten und Kompilierungsfehler beheben.
Phase 2: Statische Analyse
Checkstyle, PMD, SpotBugs (Maven)
mvn checkstyle:check pmd:check spotbugs:check
SonarQube (falls konfiguriert)
mvn sonar:sonar \
-Dsonar.projectKey=mein-quarkus-projekt \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=${SONAR_TOKEN}
Häufige zu adressierende Probleme
- Unbenutzte Imports oder Variablen
- Komplexe Methoden (hohe zyklomatische Komplexität)
- Potenzielle Nullpointer-Dereferenzierungen
- Von SpotBugs gemeldete Sicherheitsprobleme
Phase 3: Tests + Abdeckung
# Alle Tests ausführen
mvn clean test
# Abdeckungsbericht generieren
mvn jacoco:report
# Abdeckungsschwellwert durchsetzen (80%)
mvn jacoco:check
# Oder mit Gradle
./gradlew test jacocoTestReport jacocoTestCoverageVerification
Testkategorien
Unit-Tests
Testen der Servicelogik mit gemockten Abhängigkeiten:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock UserRepository userRepository;
@InjectMocks UserService userService;
@Test
void createUser_validInput_returnsUser() {
var dto = new CreateUserDto("Alice", "[email protected]");
// Panache persist() ist void — doNothing + verify verwenden
doNothing().when(userRepository).persist(any(User.class));
User result = userService.create(dto);
assertThat(result.name).isEqualTo("Alice");
verify(userRepository).persist(any(User.class));
}
}
Integrationstests
Testen mit einer echten Datenbank (Testcontainers):
@QuarkusTest
@QuarkusTestResource(PostgresTestResource.class)
class UserRepositoryIntegrationTest {
@Inject
UserRepository userRepository;
@Test
@Transactional
void findByEmail_existingUser_returnsUser() {
User user = new User();
user.name = "Alice";
user.email = "[email protected]";
userRepository.persist(user);
Optional<User> found = userRepository.findByEmail("[email protected]");
assertThat(found).isPresent();
assertThat(found.get().name).isEqualTo("Alice");
}
}
API-Tests
Testen von REST-Endpunkten mit REST Assured:
@QuarkusTest
class UserResourceTest {
@Test
void createUser_validInput_returns201() {
given()
.contentType(ContentType.JSON)
.body("""
{"name": "Alice", "email": "[email protected]"}
""")
.when().post("/api/users")
.then()
.statusCode(201)
.body("name", equalTo("Alice"));
}
@Test
void createUser_invalidEmail_returns400() {
given()
.contentType(ContentType.JSON)
.body("""
{"name": "Alice", "email": "invalid"}
""")
.when().post("/api/users")
.then()
.statusCode(400);
}
}
Abdeckungsbericht
target/site/jacoco/index.html für detaillierte Abdeckung prüfen:
- Gesamte Zeilenabdeckung (Ziel: 80%+)
- Branch-Abdeckung (Ziel: 70%+)
- Nicht abgedeckte kritische Pfade identifizieren
Phase 4: Sicherheitsscan
Abhängigkeits-Schwachstellen (Maven)
mvn org.owasp:dependency-check-maven:check
target/dependency-check-report.html auf CVEs überprüfen.
Quarkus-Sicherheitsaudit
# Verwundbare Erweiterungen prüfen
mvn quarkus:audit
# Alle Erweiterungen auflisten
mvn quarkus:list-extensions
OWASP ZAP (API-Sicherheitstests)
docker run -t ghcr.io/zaproxy/zaproxy:stable zap-api-scan.py \
-t http://localhost:8080/q/openapi \
-f openapi
Häufige Sicherheitsprüfungen
- Alle Secrets in Umgebungsvariablen (nicht im Code)
- Eingabevalidierung an allen Endpunkten
- Authentifizierung/Autorisierung konfiguriert
- CORS richtig konfiguriert
- Sicherheitsheader gesetzt
- Passwörter mit BCrypt gehasht
- SQL-Injection-Schutz (parametrisierte Abfragen)
- Ratenbegrenzung an öffentlichen Endpunkten
Phase 5: Native Kompilierung
GraalVM Native-Image-Kompatibilität testen:
# Native ausführbare Datei bauen
mvn package -Dnative
# Oder mit Container
mvn package -Dnative -Dquarkus.native.container-build=true
# Native ausführbare Datei testen
./target/*-runner
# Einfache Smoke-Tests ausführen
curl http://localhost:8080/q/health/live
curl http://localhost:8080/q/health/ready
Fehlerbehebung bei nativen Images
Häufige Probleme:
- Reflection: Reflection-Konfiguration für dynamische Klassen hinzufügen
- Resources: Ressourcen mit
quarkus.native.resources.includeseinbinden - JNI: JNI-Klassen registrieren, wenn native Bibliotheken verwendet werden
Beispiel für eine Reflection-Konfiguration:
@RegisterForReflection(targets = {MyDynamicClass.class})
public class ReflectionConfiguration {}
Phase 6: Leistungstests
Lasttests mit K6
// load-test.js
import http from 'k6/http';
import { check } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 50 },
{ duration: '1m', target: 100 },
{ duration: '30s', target: 0 },
],
};
export default function () {
const res = http.get('http://localhost:8080/api/markets');
check(res, {
'Status ist 200': (r) => r.status === 200,
'Antwortzeit < 200ms': (r) => r.timings.duration < 200,
});
}
Ausführen:
k6 run load-test.js
Zu überwachende Metriken
- Antwortzeit (p50, p95, p99)
- Durchsatz (Anfragen/Sekunde)
- Fehlerrate
- Speichernutzung
- CPU-Auslastung
Phase 7: Health Checks
# Liveness
curl http://localhost:8080/q/health/live
# Readiness
curl http://localhost:8080/q/health/ready
# Alle Health Checks
curl http://localhost:8080/q/health
# Metriken (falls aktiviert)
curl http://localhost:8080/q/metrics
Erwartete Antworten:
{
"status": "UP",
"checks": [
{
"name": "Datenbankverbindung",
"status": "UP"
}
]
}
Phase 8: Container-Image-Build
# Container-Image bauen
mvn package -Dquarkus.container-image.build=true
# Oder mit spezifischer Registry
mvn package \
-Dquarkus.container-image.build=true \
-Dquarkus.container-image.registry=docker.io \
-Dquarkus.container-image.group=myorg \
-Dquarkus.container-image.tag=1.0.0
# Container testen
docker run -p 8080:8080 myorg/my-quarkus-app:1.0.0
Container-Sicherheitsscan
# Trivy
trivy image myorg/my-quarkus-app:1.0.0
# Grype
grype myorg/my-quarkus-app:1.0.0
Phase 9: Konfigurationsvalidierung
# Alle Konfigurationseigenschaften prüfen
mvn quarkus:info
# Alle Konfigurationsquellen auflisten
curl http://localhost:8080/q/dev/io.quarkus.quarkus-vertx-http/config
Umgebungsspezifische Prüfungen
- Datenbank-URLs pro Umgebung konfiguriert
- Secrets externalisiert (Vault, Umgebungsvariablen)
- Logging-Stufen angemessen
- CORS-Ursprünge korrekt gesetzt
- Ratenbegrenzung konfiguriert
- Monitoring/Tracing aktiviert
Phase 10: Dokumentationsprüfung
- OpenAPI/Swagger-Dokumentation aktuell (
/q/swagger-ui) - README enthält Setup-Anweisungen
- API-Änderungen dokumentiert
- Migrationsleitfaden für bahnbrechende Änderungen
- Konfigurationseigenschaften dokumentiert
OpenAPI-Spezifikation generieren:
curl http://localhost:8080/q/openapi -o openapi.json
Verifizierungs-Checkliste
Codequalität
- Build ohne Warnungen erfolgreich
- Statische Analyse sauber (keine hohen/mittleren Probleme)
- Code folgt Team-Konventionen
- Kein auskommentierter Code oder TODOs im PR
Tests
- Alle Tests bestanden
- Codeabdeckung ≥ 80%
- Integrationstests mit echter Datenbank
- Sicherheitstests bestanden
- Leistung innerhalb akzeptabler Grenzen
Sicherheit
- Keine Abhängigkeits-Schwachstellen
- Authentifizierung/Autorisierung getestet
- Eingabevalidierung vollständig
- Secrets nicht im Quellcode
- Sicherheitsheader konfiguriert
Deployment
- Native Kompilierung erfolgreich
- Container-Image erstellt
- Health Checks antworten korrekt
- Konfiguration für Zielumgebung gültig
Natives Image
- Native ausführbare Datei erstellt
- Native Tests bestanden
- Startzeit < 100ms
- Speicherverbrauch akzeptabel
Automatisiertes Verifizierungs-Skript
#!/bin/bash
set -e
echo "=== Phase 1: Build ==="
mvn clean verify -DskipTests
echo "=== Phase 2: Statische Analyse ==="
mvn checkstyle:check pmd:check spotbugs:check
echo "=== Phase 3: Tests + Abdeckung ==="
mvn test jacoco:report jacoco:check
echo "=== Phase 4: Sicherheitsscan ==="
mvn org.owasp:dependency-check-maven:check
echo "=== Phase 5: Native Kompilierung ==="
mvn package -Dnative -Dquarkus.native.container-build=true
echo "=== Alle Phasen abgeschlossen ==="
echo "Berichte prüfen:"
echo " - Abdeckung: target/site/jacoco/index.html"
echo " - Sicherheit: target/dependency-check-report.html"
echo " - Native: target/*-runner"
CI/CD-Integration
GitHub Actions Beispiel
name: Verifizierung
on: [push, pull_request]
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: JDK 21 einrichten
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
- name: Maven-Pakete zwischenspeichern
uses: actions/cache@v6
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
- name: Build
run: mvn clean verify -DskipTests
- name: Test mit Abdeckung
run: mvn test jacoco:report jacoco:check
- name: Sicherheitsscan
run: mvn org.owasp:dependency-check-maven:check
- name: Abdeckung hochladen
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: target/site/jacoco/jacoco.xml
Best Practices
- Verifizierungsschleife vor jedem PR ausführen
- In der CI/CD-Pipeline automatisieren
- Probleme sofort beheben; keine Schulden anhäufen
- Abdeckung über 80% halten
- Abhängigkeiten regelmäßig aktualisieren
- Native Kompilierung regelmäßig testen
- Leistungstrends überwachen
- Bahnbrechende Änderungen dokumentieren
- Ergebnisse des Sicherheitsscans überprüfen
- Konfiguration für jede Umgebung validieren