Agent skill

Quarkus TDD

by affaan-m in affaan-m/ECC

JUnit 5、Mockito、REST Assured、Camelテスト、JaCoCoを使用したQuarkus 3.xのテスト駆動開発。機能追加、バグ修正、またはイベント駆動サービスのリファクタリング時に使用。

MITAuto-check passedTesting & QA

Install Quarkus TDD

skills CLI
$ npx skills add affaan-m/ECC --skill quarkus-tdd -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install affaan-m/ECC quarkus-tdd --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/affaan-m/ECC.git skills-src && mkdir -p .claude/skills && cp -r skills-src/docs/ja-JP/skills/quarkus-tdd .claude/skills/quarkus-tdd && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
quarkus-tdd
GitHub stars
276k
Token cost
~5.8k tokens
SKILL.md length
184 words
Files
1
Skills in repo
683
Repo updated
First seen
Licence
MIT

At a glance

JUnit 5、Mockito、REST Assured、Camelテスト、JaCoCoを使用したQuarkus 3.xのテスト駆動開発。機能追加、バグ修正、またはイベント駆動サービスのリファクタリング時に使用。

  • Works in 4 steps: テストを先に書く(失敗するはず) → 最小限のコードで合格実装 → テストが緑の状態でリファクタリング → …
  • Tasks that involve Test-driven development
  • SKILL.md covers When to Use, Workflow, Unit Tests with @Nested… and Testing Camel Routes, plus 7 more sections
  • Calls mvn

What it does

Quarkus TDD is an agent skill from affaan-m/ECC. JUnit 5、Mockito、REST Assured、Camelテスト、JaCoCoを使用したQuarkus 3.xのテスト駆動開発。機能追加、バグ修正、またはイベント駆動サービスのリファクタリング時に使用。

Its SKILL.md is about 5.8k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Testing & QA, covering Test-driven development and API testing. It works with JUnit. The repository describes itself as: The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond. The licence is MIT.

When your agent uses it

  • Tasks that involve Test-driven development
  • Tasks that involve API testing

Example prompts

  • “/quarkus-tdd”

Workflow steps

4 steps, taken from the first numbered list in SKILL.md.

  1. テストを先に書く(失敗するはず)
  2. 最小限のコードで合格実装
  3. テストが緑の状態でリファクタリング
  4. JaCoCoでカバレッジ実装(80%以上を目標)

What it can do on your machine

Read from SKILL.md and the folder at commit 4eb71d9. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • mvn

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Quarkus TDD loads about 5.8k tokens when it runs. Until then it costs about 30 tokens; SKILL.md has 184 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~30
When it runs · the whole SKILL.md, loaded when a task matches
~5.8k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from affaan-m/ECC at commit 4eb71d9, republished under its MIT licence (© affaan-m). 184 words, ~5,789 tokens.

Download SKILL.mdSave it as .claude/skills/quarkus-tdd/SKILL.md (or your agent's skills folder).
name
quarkus-tdd
description
JUnit 5、Mockito、REST Assured、Camelテスト、JaCoCoを使用したQuarkus 3.xのテスト駆動開発。機能追加、バグ修正、またはイベント駆動サービスのリファクタリング時に使用。
origin
ECC

Quarkus TDD Workflow

80%以上のカバレッジ(ユニット+統合)を備えたQuarkus 3.xサービスのTDD指導。Apache Camelを使用したイベント駆動アーキテクチャに最適化。

When to Use

  • 新機能またはRESTエンドポイント
  • バグ修正またはリファクタリング
  • データアクセスロジック、セキュリティルール、またはリアクティブストリーム追加
  • Apache Camelルートとイベントハンドラーテスト
  • RabbitMQを使用したイベント駆動サービステスト
  • 条件フローロジック検証
  • CompletableFuture非同期操作検証
  • LogContextプロパゲーション テスト

Workflow

  1. テストを先に書く(失敗するはず)
  2. 最小限のコードで合格実装
  3. テストが緑の状態でリファクタリング
  4. JaCoCoでカバレッジ実装(80%以上を目標)

Unit Tests with @Nested Organization

包括的で読みやすいテストのため、以下の構造化されたアプローチに従います:

java
@ExtendWith(MockitoExtension.class)
@DisplayName("OrderService Unit Tests")
class OrderServiceTest {

  @Mock
  private OrderRepository orderRepository;

  @Mock
  private EventService eventService;

  @Mock
  private FulfillmentPublisher fulfillmentPublisher;

  @InjectMocks
  private OrderService orderService;

  private CreateOrderCommand validCommand;

  @BeforeEach
  void setUp() {
    validCommand = new CreateOrderCommand(
        "customer-123",
        List.of(new OrderLine("sku-123", 2))
    );
  }

  @Nested
  @DisplayName("createOrder のテスト")
  class CreateOrder {

    @Test
    @DisplayName("有効なコマンドが与えられた場合、注文を永続化してフルフィルメントイベントを発行する")
    void givenValidCommand_whenCreateOrder_thenPersistsAndPublishes() {
      // ARRANGE
      doNothing().when(orderRepository).persist(any(Order.class));

      // ACT
      OrderReceipt receipt = orderService.createOrder(validCommand);

      // ASSERT
      assertThat(receipt).isNotNull();
      assertThat(receipt.customerId()).isEqualTo("customer-123");
      verify(orderRepository).persist(any(Order.class));
      verify(fulfillmentPublisher).publishAsync(receipt);
      verify(eventService).createSuccessEvent(receipt, "ORDER_CREATED");
    }

    @Test
    @DisplayName("顧客IDが無い場合、BadRequestをスロー")
    void givenMissingCustomerId_whenCreateOrder_thenThrowsBadRequest() {
      // ARRANGE
      CreateOrderCommand invalid = new CreateOrderCommand("", validCommand.lines());

      // ACT & ASSERT
      WebApplicationException exception = assertThrows(
          WebApplicationException.class,
          () -> orderService.createOrder(invalid)
      );

      assertThat(exception.getResponse().getStatus()).isEqualTo(400);
      verify(orderRepository, never()).persist(any(Order.class));
      verify(fulfillmentPublisher, never()).publishAsync(any());
    }

    @Test
    @DisplayName("永続化失敗時、エラーイベントを記録")
    void givenPersistenceFailure_whenCreateOrder_thenRecordsErrorEvent() {
      // ARRANGE
      doThrow(new PersistenceException("database unavailable"))
          .when(orderRepository).persist(any(Order.class));

      // ACT & ASSERT
      PersistenceException exception = assertThrows(
          PersistenceException.class,
          () -> orderService.createOrder(validCommand)
      );

      assertThat(exception.getMessage()).contains("database unavailable");
      verify(eventService).createErrorEvent(
          eq(validCommand),
          eq("ORDER_CREATE_FAILED"),
          contains("database unavailable")
      );
      verify(fulfillmentPublisher, never()).publishAsync(any());
    }

    @Test
    @DisplayName("nullコマンドが与えられた場合、NullPointerExceptionをスロー")
    void givenNullCommand_whenCreateOrder_thenThrowsNullPointerException() {
      // ACT & ASSERT
      assertThrows(
          NullPointerException.class,
          () -> orderService.createOrder(null)
      );

      verify(orderRepository, never()).persist(any(Order.class));
    }
  }
}
Key Testing Patterns
  1. @Nested クラス: テストするメソッド別にテストをグループ化
  2. @DisplayName: テストレポート用の読みやすい説明提供
  3. 命名規則: 明確性のため givenX_whenY_thenZ
  4. AAA パターン: 明示的な // ARRANGE, // ACT, // ASSERT コメント
  5. @BeforeEach: 重複削減のためテストデータを共通設定
  6. assertDoesNotThrow: 例外をキャッチせずに成功シナリオをテスト
  7. assertThrows: AssertJを使用したメッセージ検証で例外シナリオをテスト
  8. 包括的カバレッジ: 正常系、null入力、エッジケース、例外をテスト
  9. 相互作用検証: Mockito verify() でメソッド呼び出しが正しく行われたか確認
  10. Never検証: never() でエラーシナリオでメソッドが呼ばれていないことを確認

Testing Camel Routes

java
@QuarkusTest
@DisplayName("Business Rules Camel Route Tests")
class BusinessRulesRouteTest {

  @Inject
  CamelContext camelContext;

  @Inject
  ProducerTemplate producerTemplate;

  @InjectMock
  EventService eventService;

  @InjectMock
  DocumentValidator documentValidator;

  private BusinessRulesPayload testPayload;

  @BeforeEach
  void setUp() {
    // ARRANGE - テストデータ
    testPayload = new BusinessRulesPayload();
    testPayload.setDocumentId(1L);
    testPayload.setFlowProfile(FlowProfile.BASIC);
  }

  @Nested
  @DisplayName("business-rules-publisher ルートのテスト")
  class BusinessRulesPublisher {

    @Test
    @DisplayName("有効なペイロードが与えられた場合、メッセージをRabbitMQに送信")
    void givenValidPayload_whenPublish_thenMessageSentToQueue() throws Exception {
      // ARRANGE
      MockEndpoint mockRabbitMQ = camelContext.getEndpoint("mock:rabbitmq", MockEndpoint.class);
      mockRabbitMQ.expectedMessageCount(1);

      // テスト用の実エンドポイントをモックに置き換え
      camelContext.getRouteController().stopRoute("business-rules-publisher");
      AdviceWith.adviceWith(camelContext, "business-rules-publisher", advice -> {
        advice.replaceFromWith("direct:business-rules-publisher");
        advice.weaveByToString(".*spring-rabbitmq.*").replace().to("mock:rabbitmq");
      });
      camelContext.getRouteController().startRoute("business-rules-publisher");

      // ACT
      producerTemplate.sendBody("direct:business-rules-publisher", testPayload);

      // ASSERT — .marshal().json(JsonLibrary.Jackson)の後、bodyはJSON文字列
      mockRabbitMQ.assertIsSatisfied(5000);

      assertThat(mockRabbitMQ.getExchanges()).hasSize(1);
      String body = mockRabbitMQ.getExchanges().get(0).getIn().getBody(String.class);
      assertThat(body).contains("\"documentId\":1");
    }

    @Test
    @DisplayName("ペイロード与えられた場合、JSONに整形")
    void givenPayload_whenPublish_thenMarshalledToJson() throws Exception {
      // ARRANGE
      MockEndpoint mockMarshal = new MockEndpoint("mock:marshal");
      camelContext.addEndpoint("mock:marshal", mockMarshal);
      mockMarshal.expectedMessageCount(1);

      camelContext.getRouteController().stopRoute("business-rules-publisher");
      AdviceWith.adviceWith(camelContext, "business-rules-publisher", advice -> {
        advice.weaveAddLast().to("mock:marshal");
      });
      camelContext.getRouteController().startRoute("business-rules-publisher");

      // ACT
      producerTemplate.sendBody("direct:business-rules-publisher", testPayload);

      // ASSERT
      mockMarshal.assertIsSatisfied(5000);

      String body = mockMarshal.getExchanges().get(0).getIn().getBody(String.class);
      assertThat(body).contains("\"documentId\":1");
      assertThat(body).contains("\"flowProfile\":\"BASIC\"");
    }
  }

  @Nested
  @DisplayName("document-processing ルートのテスト")
  class DocumentProcessing {

    @Test
    @DisplayName("請求書タイプが与えられた場合、正しいプロセッサーにルーティング")
    void givenInvoiceType_whenProcess_thenRoutesToInvoiceProcessor() throws Exception {
      // ARRANGE
      MockEndpoint mockInvoice = camelContext.getEndpoint("mock:invoice", MockEndpoint.class);
      mockInvoice.expectedMessageCount(1);

      camelContext.getRouteController().stopRoute("document-processing");
      AdviceWith.adviceWith(camelContext, "document-processing", advice -> {
        advice.weaveByToString(".*direct:process-invoice.*").replace().to("mock:invoice");
      });
      camelContext.getRouteController().startRoute("document-processing");

      // ACT
      producerTemplate.sendBodyAndHeader("direct:process-document",
          testPayload, "documentType", "INVOICE");

      // ASSERT
      mockInvoice.assertIsSatisfied(5000);
    }

    @Test
    @DisplayName("検証エラーが与えられた場合、エラーハンドラーにルーティング")
    void givenValidationError_whenProcess_thenRoutesToErrorHandler() throws Exception {
      // ARRANGE
      MockEndpoint mockError = camelContext.getEndpoint("mock:error", MockEndpoint.class);
      mockError.expectedMessageCount(1);

      camelContext.getRouteController().stopRoute("document-processing");
      AdviceWith.adviceWith(camelContext, "document-processing", advice -> {
        advice.weaveByToString(".*direct:validation-error-handler.*")
            .replace().to("mock:error");
      });
      camelContext.getRouteController().startRoute("document-processing");

      // バリデータビーンをモック化して例外をスロー
      when(documentValidator.validate(any())).thenThrow(new ValidationException("Invalid document"));

      // ACT
      producerTemplate.sendBody("direct:process-document", testPayload);

      // ASSERT
      mockError.assertIsSatisfied(5000);

      Exception exception = mockError.getExchanges().get(0).getException();
      assertThat(exception).isInstanceOf(ValidationException.class);
      assertThat(exception.getMessage()).contains("Invalid document");
    }
  }
}

Testing Event Services

java
@ExtendWith(MockitoExtension.class)
@DisplayName("EventService Unit Tests")
class EventServiceTest {

  @Mock
  private EventRepository eventRepository;

  @Mock
  private ObjectMapper objectMapper;

  @InjectMocks
  private EventService eventService;

  private BusinessRulesPayload testPayload;

  @BeforeEach
  void setUp() {
    // ARRANGE
    testPayload = new BusinessRulesPayload();
    testPayload.setDocumentId(1L);
  }

  @Nested
  @DisplayName("createSuccessEvent のテスト")
  class CreateSuccessEvent {

    @Test
    @DisplayName("有効なペイロードが与えられた場合、正しい属性でサクセスイベント作成")
    void givenValidPayload_whenCreateSuccessEvent_thenEventPersisted() throws Exception {
      // ARRANGE
      when(objectMapper.writeValueAsString(testPayload)).thenReturn("{\"documentId\":1}");

      // ACT
      assertDoesNotThrow(() ->
          eventService.createSuccessEvent(testPayload, "DOCUMENT_PROCESSED"));

      // ASSERT
      verify(eventRepository).persist(argThat(event ->
          event.getType().equals("DOCUMENT_PROCESSED") &&
          event.getStatus() == EventStatus.SUCCESS &&
          event.getPayload().equals("{\"documentId\":1}") &&
          event.getTimestamp() != null
      ));
    }

    @Test
    @DisplayName("nullペイロードが与えられた場合、例外をスロー")
    void givenNullPayload_whenCreateSuccessEvent_thenThrowsException() {
      // ARRANGE
      Object nullPayload = null;

      // ACT & ASSERT
      NullPointerException exception = assertThrows(
          NullPointerException.class,
          () -> eventService.createSuccessEvent(nullPayload, "EVENT_TYPE")
      );

      assertThat(exception.getMessage()).isEqualTo("Payload cannot be null");
      verify(eventRepository, never()).persist(any());
    }
  }

  @Nested
  @DisplayName("createErrorEvent のテスト")
  class CreateErrorEvent {

    @Test
    @DisplayName("エラーが与えられた場合、エラーメッセージ付きエラーイベント作成")
    void givenError_whenCreateErrorEvent_thenEventPersistedWithMessage() throws Exception {
      // ARRANGE
      String errorMessage = "Processing failed";
      when(objectMapper.writeValueAsString(testPayload)).thenReturn("{\"documentId\":1}");

      // ACT
      assertDoesNotThrow(() ->
          eventService.createErrorEvent(testPayload, "PROCESSING_ERROR", errorMessage));

      // ASSERT
      verify(eventRepository).persist(argThat(event ->
          event.getType().equals("PROCESSING_ERROR") &&
          event.getStatus() == EventStatus.ERROR &&
          event.getErrorMessage().equals(errorMessage) &&
          event.getPayload().equals("{\"documentId\":1}")
      ));
    }

    @ParameterizedTest
    @DisplayName("不正なエラーメッセージが与えられた場合、例外をスロー")
    @ValueSource(strings = {"", " "})
    void givenBlankErrorMessage_whenCreateErrorEvent_thenThrowsException(String blankMessage) {
      // ACT & ASSERT
      IllegalArgumentException exception = assertThrows(
          IllegalArgumentException.class,
          () -> eventService.createErrorEvent(testPayload, "ERROR", blankMessage)
      );

      assertThat(exception.getMessage()).contains("Error message cannot be blank");
    }
  }
}

Testing CompletableFuture

java
@ExtendWith(MockitoExtension.class)
@DisplayName("FileStorageService Unit Tests")
class FileStorageServiceTest {

  @Mock
  private S3Client s3Client;

  @Mock
  private ExecutorService executorService;

  @InjectMocks
  private FileStorageService fileStorageService;

  private InputStream testInputStream;
  private LogContext testLogContext;

  @BeforeEach
  void setUp() {
    // ARRANGE
    testInputStream = new ByteArrayInputStream("test content".getBytes());
    testLogContext = new LogContext();
    testLogContext.put("traceId", "trace-123");
  }

  @Nested
  @DisplayName("uploadOriginalFile のテスト")
  class UploadOriginalFile {

    @Test
    @DisplayName("有効なファイルが与えられた場合、ファイルアップロード成功とドキュメント情報を返す")
    void givenValidFile_whenUpload_thenReturnsDocumentInfo() throws Exception {
      // ARRANGE
      doAnswer(invocation -> {
        ((Runnable) invocation.getArgument(0)).run();
        return null;
      }).when(executorService).execute(any(Runnable.class));

      when(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class)))
          .thenReturn(PutObjectResponse.builder().build());

      // ACT
      CompletableFuture<StoredDocumentInfo> future =
          fileStorageService.uploadOriginalFile(testInputStream, 1024L,
              testLogContext, InvoiceFormat.UBL);

      StoredDocumentInfo result = future.join();

      // ASSERT
      assertThat(result).isNotNull();
      assertThat(result.getPath()).isNotBlank();
      assertThat(result.getSize()).isEqualTo(1024L);
      assertThat(result.getUploadedAt()).isNotNull();

      verify(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class));
    }

    @Test
    @DisplayName("S3アップロード失敗が与えられた場合、CompletableFutureが失敗")
    void givenS3Failure_whenUpload_thenCompletableFutureFails() {
      // ARRANGE — 例外がfutureを通じてプロパゲートされるように同期実行
      doAnswer(invocation -> {
        ((Runnable) invocation.getArgument(0)).run();
        return null;
      }).when(executorService).execute(any(Runnable.class));

      when(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class)))
          .thenThrow(new StorageException("S3 unavailable"));

      // ACT
      CompletableFuture<StoredDocumentInfo> future =
          fileStorageService.uploadOriginalFile(testInputStream, 1024L,
              testLogContext, InvoiceFormat.UBL);

      // ASSERT
      assertThatThrownBy(() -> future.join())
          .isInstanceOf(CompletionException.class)
          .hasCauseInstanceOf(StorageException.class)
          .hasMessageContaining("S3 unavailable");
    }

    @Test
    @DisplayName("LogContextが与えられた場合、非同期操作にコンテキストをプロパゲート")
    void givenLogContext_whenUpload_thenContextPropagated() throws Exception {
      // ARRANGE
      AtomicReference<LogContext> capturedContext = new AtomicReference<>();

      doAnswer(invocation -> {
        capturedContext.set(CustomLog.getCurrentContext());
        ((Runnable) invocation.getArgument(0)).run();
        return null;
      }).when(executorService).execute(any(Runnable.class));

      // ACT
      fileStorageService.uploadOriginalFile(testInputStream, 1024L,
          testLogContext, InvoiceFormat.UBL).join();

      // ASSERT
      assertThat(capturedContext.get()).isNotNull();
      assertThat(capturedContext.get().get("traceId")).isEqualTo("trace-123");
    }
  }
}

Resource Layer Tests (REST Assured)

java
@QuarkusTest
@DisplayName("DocumentResource API Tests")
class DocumentResourceTest {

  @InjectMock
  DocumentService documentService;

  @Nested
  @DisplayName("GET /api/documents のテスト")
  class ListDocuments {

    @Test
    @DisplayName("ドキュメントが存在する場合、ドキュメント一覧を返す")
    void givenDocumentsExist_whenList_thenReturnsOk() {
      // ARRANGE
      List<Document> documents = List.of(createDocument(1L, "DOC-001"));
      when(documentService.list(0, 20)).thenReturn(documents);

      // ACT & ASSERT
      given()
          .when().get("/api/documents")
          .then()
          .statusCode(200)
          .body("$.size()", is(1))
          .body("[0].referenceNumber", equalTo("DOC-001"));
    }
  }

  @Nested
  @DisplayName("POST /api/documents のテスト")
  class CreateDocument {

    @Test
    @DisplayName("有効なリクエストが与えられた場合、ドキュメント作成して201を返す")
    void givenValidRequest_whenCreate_thenReturns201() {
      // ARRANGE
      Document document = createDocument(1L, "DOC-001");
      when(documentService.create(any())).thenReturn(document);

      // ACT & ASSERT
      given()
          .contentType(ContentType.JSON)
          .body("""
              {
                "referenceNumber": "DOC-001",
                "description": "Test document",
                "validUntil": "2030-01-01T00:00:00Z",
                "categories": ["test"]
              }
              """)
          .when().post("/api/documents")
          .then()
          .statusCode(201)
          .header("Location", containsString("/api/documents/1"))
          .body("referenceNumber", equalTo("DOC-001"));
    }

    @Test
    @DisplayName("不正なリクエストが与えられた場合、400を返す")
    void givenInvalidRequest_whenCreate_thenReturns400() {
      // ACT & ASSERT
      given()
          .contentType(ContentType.JSON)
          .body("""
              {
                "referenceNumber": "",
                "description": "Test"
              }
              """)
          .when().post("/api/documents")
          .then()
          .statusCode(400);
    }
  }

  private Document createDocument(Long id, String referenceNumber) {
    Document document = new Document();
    document.setId(id);
    document.setReferenceNumber(referenceNumber);
    document.setStatus(DocumentStatus.PENDING);
    return document;
  }
}

Integration Tests with Real Database

java
@QuarkusTest
@TestProfile(IntegrationTestProfile.class)
@DisplayName("Document Integration Tests")
class DocumentIntegrationTest {

  @Test
  @Transactional
  @DisplayName("新規ドキュメントをAPIで作成・取得、成功する")
  void givenNewDocument_whenCreateAndRetrieve_thenSuccessful() {
    // ACT - APIで作成
    Long id = given()
        .contentType(ContentType.JSON)
        .body("""
            {
              "referenceNumber": "INT-001",
              "description": "Integration test",
              "validUntil": "2030-01-01T00:00:00Z",
              "categories": ["test"]
            }
            """)
        .when().post("/api/documents")
        .then()
        .statusCode(201)
        .extract().path("id");

    // ASSERT - APIで取得
    given()
        .when().get("/api/documents/" + id)
        .then()
        .statusCode(200)
        .body("referenceNumber", equalTo("INT-001"));
  }
}

Coverage with JaCoCo

Maven Configuration (Complete)
xml
<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>0.8.13</version>
  <executions>
    <!-- テスト実行用エージェント準備 -->
    <execution>
      <id>prepare-agent</id>
      <goals>
        <goal>prepare-agent</goal>
      </goals>
    </execution>

    <!-- カバレッジレポート生成 -->
    <execution>
      <id>report</id>
      <phase>verify</phase>
      <goals>
        <goal>report</goal>
      </goals>
    </execution>

    <!-- カバレッジ閾値を強制 -->
    <execution>
      <id>check</id>
      <goals>
        <goal>check</goal>
      </goals>
      <configuration>
        <rules>
          <rule>
            <element>BUNDLE</element>
            <limits>
              <limit>
                <counter>LINE</counter>
                <value>COVEREDRATIO</value>
                <minimum>0.80</minimum>
              </limit>
              <limit>
                <counter>BRANCH</counter>
                <value>COVEREDRATIO</value>
                <minimum>0.70</minimum>
              </limit>
            </limits>
          </rule>
        </rules>
      </configuration>
    </execution>
  </executions>
</plugin>

カバレッジ付きテスト実行:

bash
mvn clean test
mvn jacoco:report
mvn jacoco:check

# レポート: target/site/jacoco/index.html

Test Dependencies

xml
<dependencies>
    <!-- Quarkus Testing -->
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-junit5</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-junit5-mockito</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- Mockito -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- AssertJ(JUnitアサーション推奨) -->
    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>3.24.2</version>
        <scope>test</scope>
    </dependency>

    <!-- REST Assured -->
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- Camel Testing -->
    <dependency>
        <groupId>org.apache.camel.quarkus</groupId>
        <artifactId>camel-quarkus-junit5</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Best Practices

テスト組織
  • テストするメソッド別にグループ化するため@Nestedクラス使用
  • レポートで見やすいテスト説明のため@DisplayName使用
  • テストメソッド命名はgivenX_whenY_thenZ規則に従う
  • 重複削減のため@BeforeEachで共通テストデータ設定
テスト構造
  • 明示的コメント(// ARRANGE, // ACT, // ASSERT)でAAAパターン従う
  • 成功シナリオではassertDoesNotThrow使用
  • 例外シナリオではメッセージ検証と共にassertThrows使用
  • AssertJ contains()またはisEqualTo()で例外メッセージ検証
テストカバレッジ
  • 全パブリックメソッドの正常系パスをテスト
  • null入力ハンドリングテスト
  • エッジケース(空のコレクション、境界値、負のID、空文字列)テスト
  • 例外シナリオを包括的にテスト
  • 外部依存関係(リポジトリ、サービス、Camelエンドポイント)をモック化
  • 80%以上の行カバレッジ、70%以上のブランチカバレッジ目指す
アサーション
  • AssertJ推奨(JUnitアサーション代わりにassertThat使用)
  • 読みやすさのため流暢なAssertJ API使用:assertThat(list).hasSize(3).contains(item)
  • 例外は、JUnit assertThrowsでキャプチャ、AssertJでメッセージ検証
  • 非スロー成功パスはJUnit assertDoesNotThrow使用
  • コレクションにはextracting(), filteredOn(), containsExactly()使用
統合テスト
  • 統合テスト用に@QuarkusTest使用
  • Quarkusテストの依存関係モック化に@InjectMock使用
  • APIテストに REST Assured優先使用
  • テスト固有設定に@TestProfile使用
イベント駆動テスト
  • AdviceWithとMockEndpointでCamelルートテスト
  • 必要に応じて@CamelQuarkusTest注釈使用(スタンドアロンCamelテスト)
  • メッセージ内容、ヘッダー、ルーティングロジック検証
  • エラーハンドリングルートを別個にテスト
  • ユニットテストで外部システム(RabbitMQ、S3、データベース)をモック化
Camel ルートテスト
  • メッセージフロー確認にMockEndpoint使用
  • テスト用ルート変更にエンドポイントをモックに置き換えるAdviceWith使用
  • メッセージ変換と整形テスト
  • 例外処理とデッドレターキューテスト
非同期操作テスト
  • CompletableFutureの成功・失敗シナリオテスト
  • 非同期完了待機に.join()使用
  • CompletableFutureから例外プロパゲーション検証
  • 非同期操作へのLogContextプロパゲーション検証
パフォーマンス
  • テストを高速で分離した状態に保つ
  • 継続モードでテスト実行:mvn quarkus:test
  • 入力バリエーション用に パラメータ化テスト(@ParameterizedTest)使用
  • 再利用可能なテストデータビルダーまたはファクトリメソッド構築
Quarkus固有
  • 最新LTSバージョン(Quarkus 3.x)に留める
  • 定期的にネイティブコンパイル互換性テスト
  • 異なるシナリオでQuarkusテストプロファイル活用
  • ローカルテストにQuarkus dev サービス活用
  • @MockBean代わりに@InjectMock(Quarkus固有)使用
検証ベストプラクティス
  • モック化された依存関係の相互作用は常に検証
  • エラーシナリオでメソッドが呼ばれていないことを確認するにverify(mock, never())使用
  • 複雑な引数マッチングにargThat()使用
  • 呼び出し順序が重要な場合InOrder(Mockitoから)で検証

© affaan-m, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in docs/ja-JP/skills/quarkus-tdd of affaan-m/ECC.

Open the folder on GitHubat commit 4eb71d9

Compare with similar skills

Quarkus TDD next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Quarkus TDD compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Quarkus TDD this skillaffaan-m/ECC276k—~5.8kAutomated safety check: PassMIT
TDD Guidealirezarezvani/claude-skills28k—~3.4kAutomated safety check: PassMIT
Postman Newman Automationsickn33/agentic-awesome-skills47k1 repos~2.1kAutomated safety check: PassMIT
TDD GuideLeoYeAI/openclaw-master-skills2.2k—~1.4kAutomated safety check: PassMIT
TDD Guideborghei/Claude-Skills891—~1.7kAutomated safety check: PassMIT
Pixiv CLI TestFlanChanXwO/pixiv-cli134—~2.1kAutomated safety check: PassMIT

Similar skills

  • TDD Guide

    alirezarezvani/claude-skills

    Test-driven development skill for writing unit tests, generating test fixtures and mocks, analyzing coverage gaps, and guiding red-green-refactor workflows across Jest, Pytest, JUnit, Vitest, and…

    28k GitHub stars~3.4k tokensUpdated 1 mo ago
    Testing & QAAuto-check passed
  • Postman Newman Automation

    sickn33/agentic-awesome-skills

    Generate Newman CLI commands, configuration files, Jenkins pipeline scripts, and shell automation for running Postman collections in CI/CD or local environments.

    47k GitHub starsUsed in 1 repo~2.1k tokens
    Testing & QAAuto-check passed
  • TDD Guide

    LeoYeAI/openclaw-master-skills

    Test-driven development skill for writing unit tests, generating test fixtures and mocks, analyzing coverage gaps, and guiding red-green-refactor workflows across Jest, Pytest, JUnit, Vitest, and…

    2.2k GitHub stars~1.4k tokensUpdated 2 mo ago
    Testing & QAAuto-check passed
  • TDD Guide

    borghei/Claude-Skills

    Guide red-green-refactor TDD with test generation, coverage-gap analysis, and multi- framework support.

    891 GitHub stars~1.7k tokensUpdated 4 days ago
    Testing & QAAuto-check passed
  • Pixiv CLI Test

    FlanChanXwO/pixiv-cli

    Select and run pixiv-cli regression, TDD, document, SDK, CLI/MCP, and build checks.

    134 GitHub stars~2.1k tokensUpdated yesterday
    Testing & QAAuto-check passed
  • TDD Guide

    aAAaqwq/AGI-Super-Team

    Test-driven development workflow with test generation, coverage analysis, and multi-framework support

    105 GitHub starsUsed in 2 repos~1.1k tokens
    Testing & QAAuto-check passed

More from affaan-m/ECC

All 682 skills in this repo
  • Skill Stocktake

    affaan-m/ECC

    Audits your installed Claude skills and commands for quality, with a quick mode for recently changed skills and a full mode that evaluates all of them through subagents.

    277k GitHub starsUsed in 5 repos~3.1k tokens
    Auto-check passed
  • Ingests, indexes, searches, edits and monitors video, audio and live streams through the VideoDB Python SDK, returning stream links, clips and timestamps.

    277k GitHub starsUsed in 3 repos~3.5k tokens
    Auto-check: notes
  • Docs Governance

    affaan-m/ECC

    Route broad documentation-governance requests to existing ECC skills and run an opt-in, read-only audit of mapped documentation roles, links, ADR indexes, and evidence references.

    277k GitHub stars~1.1k tokensUpdated today
    Auto-check passed
  • Rules Distillation

    affaan-m/ECC

    Scans installed skills for principles that recur across them and proposes rule-file changes: append, revise, add a section, create a file or leave as covered.

    277k GitHub starsUsed in 2 repos~2.3k tokens
    Auto-check passed
  • Builds DRAFT counterparty agreements from one markdown template and a small JSON spec per party, with clauses picked by the party's role.

    277k GitHub stars~2.9k tokensUpdated today
    Auto-check passed
  • Set an ECC-specific frontend design direction for production UI work.

    277k GitHub starsUsed in 1 repo~2.2k tokens
    Auto-check passed

Works with

Categories

Questions about Quarkus TDD

What does Quarkus TDD do?

JUnit 5、Mockito、REST Assured、Camelテスト、JaCoCoを使用したQuarkus 3.xのテスト駆動開発。機能追加、バグ修正、またはイベント駆動サービスのリファクタリング時に使用。. Quarkus TDD is an agent skill from affaan-m/ECC.

When should I use Quarkus TDD?

Quarkus TDD fits situations like: tasks that involve Test-driven development; tasks that involve API testing.

How do I install Quarkus TDD in Claude Code?

Run `npx skills add affaan-m/ECC --skill quarkus-tdd -a claude-code`. Or copy the skill folder (docs/ja-JP/skills/quarkus-tdd in affaan-m/ECC) into .claude/skills/quarkus-tdd in your project. Claude Code loads it when a task matches its description.

How do I install Quarkus TDD in Codex?

Run `npx skills add affaan-m/ECC --skill quarkus-tdd -a codex`. Or copy the skill folder (docs/ja-JP/skills/quarkus-tdd in affaan-m/ECC) into .agents/skills/quarkus-tdd in your project. Codex loads it when a task matches its description.

Can I use Quarkus TDD in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add affaan-m/ECC --skill quarkus-tdd -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/quarkus-tdd, .gemini/skills/quarkus-tdd, .github/skills/quarkus-tdd and .opencode/skills/quarkus-tdd in your project.

What does Quarkus TDD need to run?

Going by SKILL.md and its folder, Quarkus TDD needs the command-line tools its instructions call (mvn).

Does Quarkus TDD access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Quarkus TDD safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Quarkus TDD use?

Quarkus TDD is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Quarkus TDD use?

About 5.8k tokens (SKILL.md is roughly 23k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Quarkus TDD?

Skills that share tags, products or a category with Quarkus TDD: TDD Guide (alirezarezvani/claude-skills, 28k stars), Postman Newman Automation (sickn33/agentic-awesome-skills, 47k stars), TDD Guide (LeoYeAI/openclaw-master-skills, 2.2k stars) and TDD Guide (borghei/Claude-Skills, 891 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Quarkus TDD?

affaan-m (a GitHub user) maintains it in affaan-m/ECC, which has 276,111 GitHub stars. The repository holds 683 skills in this directory. The repository was last updated on October 10, 2026.

Source: affaan-m/ECC on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.