跳转至

Quarkus 验证循环

在 PR、重大变更之后以及生产环境之前执行。

何时启用

  • 开始 Quarkus 服务的 Pull Request 之前
  • 重大重构或依赖项升级之后
  • 在预生产/生产环境之前的预部署验证
  • 执行完整构建 → 代码检查 → 测试 → 安全扫描 → 原生编译流水线
  • 验证测试覆盖率满足阈值(80% 以上)
  • 原生镜像兼容性测试

阶段 1:构建

# Maven
mvn clean verify -DskipTests

# Gradle
./gradlew clean assemble -x test

构建失败时停止并修复编译错误。

阶段 2:静态分析

Checkstyle、PMD、SpotBugs(Maven)

mvn checkstyle:check pmd:check spotbugs:check

SonarQube(如果已配置)

mvn sonar:sonar \
  -Dsonar.projectKey=my-quarkus-project \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.login=${SONAR_TOKEN}

需要处理的常见问题

  • 未使用的导入或变量
  • 复杂方法(高圈复杂度)
  • 潜在的 null 指针解引用
  • SpotBugs 标记的安全问题

阶段 3:测试 + 覆盖率

# 全テスト実行
mvn clean test

# カバレッジレポート生成
mvn jacoco:report

# カバレッジ閾値を強制(80%)
mvn jacoco:check

# またはGradleで
./gradlew test jacocoTestReport jacocoTestCoverageVerification

测试类别

单元测试

使用模拟依赖项测试服务逻辑:

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
  @Mock UserRepository userRepository;
  @InjectMocks UserService userService;

  @Test
  void createUser_validInput_returnsUser() {
    var dto = new CreateUserDto("Alice", "alice@example.com");

    // Panacheのpersist()はvoid — doNothing + verifyを使用
    doNothing().when(userRepository).persist(any(User.class));

    User result = userService.create(dto);

    assertThat(result.name).isEqualTo("Alice");
    verify(userRepository).persist(any(User.class));
  }
}

集成测试

使用真实数据库(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 = "alice@example.com";
    userRepository.persist(user);

    Optional<User> found = userRepository.findByEmail("alice@example.com");

    assertThat(found).isPresent();
    assertThat(found.get().name).isEqualTo("Alice");
  }
}

API 测试

使用 REST Assured 测试 REST 端点:

@QuarkusTest
class UserResourceTest {

  @Test
  void createUser_validInput_returns201() {
    given()
        .contentType(ContentType.JSON)
        .body("""
            {"name": "Alice", "email": "alice@example.com"}
            """)
        .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);
  }
}

覆盖率报告

对于详细覆盖率,查看 target/site/jacoco/index.html: - 全行覆盖率(目标:80% 以上) - 分支覆盖率(目标:70% 以上) - 识别未覆盖的重要路径

阶段 4:安全扫描

依赖项漏洞(Maven)

mvn org.owasp:dependency-check-maven:check

关于 CVE,查看 target/dependency-check-report.html。

Quarkus 安全审计

# 脆弱な拡張機能をチェック
mvn quarkus:audit

# 全拡張機能をリスト
mvn quarkus:list-extensions

OWASP ZAP(API 安全测试)

docker run -t ghcr.io/zaproxy/zaproxy:stable zap-api-scan.py \
  -t http://localhost:8080/q/openapi \
  -f openapi

常见安全检查

  • [ ] 所有密钥都放在环境变量中(不在代码中)
  • [ ] 所有端点的输入验证
  • [ ] 已配置认证/授权
  • [ ] CORS 已正确配置
  • [ ] 已配置安全头
  • [ ] 使用 BCrypt 对密码进行哈希
  • [ ] SQL 注入防护(参数化查询)
  • [ ] 公开端点的速率限制

阶段 5:原生编译

GraalVM 原生镜像兼容性测试:

# ネイティブ実行ファイルビルド
mvn package -Dnative

# またはコンテナで
mvn package -Dnative -Dquarkus.native.container-build=true

# ネイティブ実行ファイルテスト
./target/*-runner

# 基本スモークテスト実行
curl http://localhost:8080/q/health/live
curl http://localhost:8080/q/health/ready

原生镜像故障排除

常见问题: - 反射:为动态类添加反射配置 - 资源:使用 quarkus.native.resources.includes 包含资源 - JNI:使用原生库时注册 JNI 类

反射配置示例:

@RegisterForReflection(targets = {MyDynamicClass.class})
public class ReflectionConfiguration {}

阶段 6:性能测试

使用 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 is 200': (r) => r.status === 200,
    'response time < 200ms': (r) => r.timings.duration < 200,
  });
}

运行:

k6 run load-test.js

需要监控的指标

  • 响应时间(p50、p95、p99)
  • 吞吐量(请求/秒)
  • 错误率
  • 内存使用量
  • CPU 使用量

阶段 7:健康检查

# Liveness
curl http://localhost:8080/q/health/live

# Readiness
curl http://localhost:8080/q/health/ready

# 全ヘルスチェック
curl http://localhost:8080/q/health

# メトリクス(有効な場合)
curl http://localhost:8080/q/metrics

预期响应:

{
  "status": "UP",
  "checks": [
    {
      "name": "Database connection",
      "status": "UP"
    }
  ]
}

阶段 8:容器镜像构建

# コンテナイメージビルド
mvn package -Dquarkus.container-image.build=true

# または特定のレジストリで
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

# コンテナテスト
docker run -p 8080:8080 myorg/my-quarkus-app:1.0.0

容器安全扫描

# Trivy
trivy image myorg/my-quarkus-app:1.0.0

# Grype
grype myorg/my-quarkus-app:1.0.0

阶段 9:配置验证

# 全設定プロパティをチェック
mvn quarkus:info

# 全設定ソースをリスト
curl http://localhost:8080/q/dev/io.quarkus.quarkus-vertx-http/config

环境特定检查

  • [ ] 数据库 URL 已按环境配置
  • [ ] 密钥已外部化(Vault、环境变量)
  • [ ] 日志级别适当
  • [ ] CORS 来源已正确配置
  • [ ] 已设置速率限制
  • [ ] 已启用监控/追踪

阶段 10:文档审查

  • [ ] OpenAPI/Swagger 文档为最新(/q/swagger-ui)
  • [ ] README 中包含设置说明
  • [ ] API 变更已文档化
  • [ ] 破坏性变更包含迁移指南
  • [ ] 配置属性已文档化

OpenAPI 规范生成:

curl http://localhost:8080/q/openapi -o openapi.json

验证清单

代码质量

  • [ ] 构建成功且无警告
  • [ ] 静态分析干净(无高/中等级问题)
  • [ ] 代码符合团队规范
  • [ ] PR 中无注释或 TODO

测试

  • [ ] 所有测试成功
  • [ ] 代码覆盖率 ≥ 80%
  • [ ] 使用真实数据库进行集成测试
  • [ ] 安全测试成功
  • [ ] 性能在可接受范围内

安全

  • [ ] 依赖项无漏洞
  • [ ] 已测试认证/授权
  • [ ] 输入验证完整
  • [ ] 源代码中无密钥
  • [ ] 已设置安全头

部署

  • [ ] 原生编译成功
  • [ ] 容器镜像构建成功
  • [ ] 健康检查正常工作
  • [ ] 目标环境配置有效

原生镜像

  • [ ] 原生可执行文件构建成功
  • [ ] 原生测试成功
  • [ ] 启动时间 < 100ms
  • [ ] 内存占用在可接受范围内

自动化验证脚本

#!/bin/bash
set -e

echo "=== Phase 1: Build ==="
mvn clean verify -DskipTests

echo "=== Phase 2: Static Analysis ==="
mvn checkstyle:check pmd:check spotbugs:check

echo "=== Phase 3: Tests + Coverage ==="
mvn test jacoco:report jacoco:check

echo "=== Phase 4: Security Scan ==="
mvn org.owasp:dependency-check-maven:check

echo "=== Phase 5: Native Compilation ==="
mvn package -Dnative -Dquarkus.native.container-build=true

echo "=== All Phases Complete ==="
echo "Review reports:"
echo "  - Coverage: target/site/jacoco/index.html"
echo "  - Security: target/dependency-check-report.html"
echo "  - Native: target/*-runner"

CI/CD 集成

GitHub Actions 示例

name: Verification

on: [push, pull_request]

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7

      - name: Set up JDK 21
        uses: actions/setup-java@v5
        with:
          java-version: '21'
          distribution: 'temurin'

      - name: Cache Maven packages
        uses: actions/cache@v6
        with:
          path: ~/.m2
          key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}

      - name: Build
        run: mvn clean verify -DskipTests

      - name: Test with Coverage
        run: mvn test jacoco:report jacoco:check

      - name: Security Scan
        run: mvn org.owasp:dependency-check-maven:check

      - name: Upload Coverage
        uses: codecov/codecov-action@v7
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: target/site/jacoco/jacoco.xml

最佳实践

  • 在所有 PR 之前运行验证循环
  • 在 CI/CD 流水线中自动化
  • 立即修复问题,不积累技术债务
  • 保持覆盖率在 80% 以上
  • 定期更新依赖项
  • 定期测试原生编译
  • 监控性能趋势
  • 文档化破坏性变更
  • 审查安全扫描结果
  • 按环境验证配置

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC