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…
JUnit 5、Mockito、REST Assured、Camelテスト、JaCoCoを使用したQuarkus 3.xのテスト駆動開発。機能追加、バグ修正、またはイベント駆動サービスのリファクタリング時に使用。
$ npx skills add affaan-m/ECC --skill quarkus-tdd -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install affaan-m/ECC quarkus-tdd --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ 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-srcUse ~/.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/
Install the "quarkus-tdd" agent skill from https://github.com/affaan-m/ECC/tree/main/docs/ja-JP/skills/quarkus-tdd into .claude/skills/quarkus-tdd/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "quarkus-tdd", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/affaan-m/ECC/tree/main/docs/ja-JP/skills/quarkus-tddType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add affaan-m/ECC --skill quarkus-tdd -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install affaan-m/ECC quarkus-tdd --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/affaan-m/ECC.git skills-src && mkdir -p .agents/skills && cp -r skills-src/docs/ja-JP/skills/quarkus-tdd .agents/skills/quarkus-tdd && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "quarkus-tdd" agent skill from https://github.com/affaan-m/ECC/tree/main/docs/ja-JP/skills/quarkus-tdd into .agents/skills/quarkus-tdd/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "quarkus-tdd", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add affaan-m/ECC --skill quarkus-tdd -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install affaan-m/ECC quarkus-tdd --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/affaan-m/ECC.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/docs/ja-JP/skills/quarkus-tdd .cursor/skills/quarkus-tdd && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "quarkus-tdd" agent skill from https://github.com/affaan-m/ECC/tree/main/docs/ja-JP/skills/quarkus-tdd into .cursor/skills/quarkus-tdd/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "quarkus-tdd", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/affaan-m/ECC.git --path docs/ja-JP/skills/quarkus-tdd--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add affaan-m/ECC --skill quarkus-tdd -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install affaan-m/ECC quarkus-tdd --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/affaan-m/ECC.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/docs/ja-JP/skills/quarkus-tdd .gemini/skills/quarkus-tdd && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "quarkus-tdd" agent skill from https://github.com/affaan-m/ECC/tree/main/docs/ja-JP/skills/quarkus-tdd into .gemini/skills/quarkus-tdd/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "quarkus-tdd", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install affaan-m/ECC quarkus-tddInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add affaan-m/ECC --skill quarkus-tdd -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/affaan-m/ECC.git skills-src && mkdir -p .github/skills && cp -r skills-src/docs/ja-JP/skills/quarkus-tdd .github/skills/quarkus-tdd && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "quarkus-tdd" agent skill from https://github.com/affaan-m/ECC/tree/main/docs/ja-JP/skills/quarkus-tdd into .github/skills/quarkus-tdd/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "quarkus-tdd", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add affaan-m/ECC --skill quarkus-tdd -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install affaan-m/ECC quarkus-tdd --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/affaan-m/ECC.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/docs/ja-JP/skills/quarkus-tdd .opencode/skills/quarkus-tdd && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "quarkus-tdd" agent skill from https://github.com/affaan-m/ECC/tree/main/docs/ja-JP/skills/quarkus-tdd into .opencode/skills/quarkus-tdd/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "quarkus-tdd", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
quarkus-tddJUnit 5、Mockito、REST Assured、Camelテスト、JaCoCoを使用したQuarkus 3.xのテスト駆動開発。機能追加、バグ修正、またはイベント駆動サービスのリファクタリング時に使用。
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.
4 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 4eb71d9. It shows what the files ask for, not the result of running them.
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.
Shell commands in SKILL.md call:
mvnFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
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.
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.
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.
The full file from affaan-m/ECC at commit 4eb71d9, republished under its MIT licence (© affaan-m). 184 words, ~5,789 tokens.
.claude/skills/quarkus-tdd/SKILL.md (or your agent's skills folder).80%以上のカバレッジ(ユニット+統合)を備えたQuarkus 3.xサービスのTDD指導。Apache Camelを使用したイベント駆動アーキテクチャに最適化。
包括的で読みやすいテストのため、以下の構造化されたアプローチに従います:
@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));
}
}
}givenX_whenY_thenZ// ARRANGE, // ACT, // ASSERT コメントverify() でメソッド呼び出しが正しく行われたか確認never() でエラーシナリオでメソッドが呼ばれていないことを確認@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");
}
}
}@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");
}
}
}@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");
}
}
}@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;
}
}@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"));
}
}<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>カバレッジ付きテスト実行:
mvn clean test
mvn jacoco:report
mvn jacoco:check
# レポート: target/site/jacoco/index.html<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>@Nestedクラス使用@DisplayName使用givenX_whenY_thenZ規則に従う// ARRANGE, // ACT, // ASSERT)でAAAパターン従うassertDoesNotThrow使用assertThrows使用contains()またはisEqualTo()で例外メッセージ検証assertThat使用)assertThat(list).hasSize(3).contains(item)assertThrowsでキャプチャ、AssertJでメッセージ検証assertDoesNotThrow使用extracting(), filteredOn(), containsExactly()使用@QuarkusTest使用@InjectMock使用@TestProfile使用AdviceWithとMockEndpointでCamelルートテスト@CamelQuarkusTest注釈使用(スタンドアロンCamelテスト)MockEndpoint使用AdviceWith使用.join()使用mvn quarkus:test@ParameterizedTest)使用@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
Just SKILL.md in docs/ja-JP/skills/quarkus-tdd of affaan-m/ECC.
Open the folder on GitHubat commit 4eb71d9
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.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Quarkus TDD this skillaffaan-m/ECC | 276k | — | ~5.8k | Automated safety check: Pass | MIT | |
| TDD Guidealirezarezvani/claude-skills | 28k | — | ~3.4k | Automated safety check: Pass | MIT | |
| Postman Newman Automationsickn33/agentic-awesome-skills | 47k | 1 repos | ~2.1k | Automated safety check: Pass | MIT | |
| TDD GuideLeoYeAI/openclaw-master-skills | 2.2k | — | ~1.4k | Automated safety check: Pass | MIT | |
| TDD Guideborghei/Claude-Skills | 891 | — | ~1.7k | Automated safety check: Pass | MIT | |
| Pixiv CLI TestFlanChanXwO/pixiv-cli | 134 | — | ~2.1k | Automated safety check: Pass | MIT |
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…
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.
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…
borghei/Claude-Skills
Guide red-green-refactor TDD with test generation, coverage-gap analysis, and multi- framework support.
FlanChanXwO/pixiv-cli
Select and run pixiv-cli regression, TDD, document, SDK, CLI/MCP, and build checks.
aAAaqwq/AGI-Super-Team
Test-driven development workflow with test generation, coverage analysis, and multi-framework support
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.
affaan-m/ECC
Ingests, indexes, searches, edits and monitors video, audio and live streams through the VideoDB Python SDK, returning stream links, clips and timestamps.
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.
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.
affaan-m/ECC
Builds DRAFT counterparty agreements from one markdown template and a small JSON spec per party, with clauses picked by the party's role.
affaan-m/ECC
Set an ECC-specific frontend design direction for production UI work.
Works with
Categories
JUnit 5、Mockito、REST Assured、Camelテスト、JaCoCoを使用したQuarkus 3.xのテスト駆動開発。機能追加、バグ修正、またはイベント駆動サービスのリファクタリング時に使用。. Quarkus TDD is an agent skill from affaan-m/ECC.
Quarkus TDD fits situations like: tasks that involve Test-driven development; tasks that involve API testing.
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.
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.
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.
Going by SKILL.md and its folder, Quarkus TDD needs the command-line tools its instructions call (mvn).
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.
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.
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.
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.
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.
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.